$J4.95
ASSEMBLY LANGUAGE
FOR KIDS
lODORE 64
by
WILLIAM B. SANDERS
ASSEMBLY LANGUAGE
FOR KIDS:
COMMODORE 64
by William B. Sanders, Ph.D.
San Diego State University
Glicrocomscribe
i\WT^ Literate Microcomputer Documentation
8982 Stimson Court
San Diego, California 92129
619/484-3884 or 619/578-4588
Library of Congress Cataloging in Publication Data
Sanders, William B.
Assembly Language For Kids: Commodore 64
Includes index
1. Commodore 64 Computer
2. Assembly Language (Computer program language)
I. Sanders, William B., 1944-
II. Title: Assembly Language for Kids:
Commodore 64
ISBN 0-931 145-00-7
© 1984 by William B. Sanders
San Diego, California
Manufactured in the United States of America
All rights reserved. No part of this book may be reproduced by
any means without the written permission of the author and
publisher.
Cover design by Dyna Pac
Typesetting by GD Enterprises
ASSEMBLY LANGUAGE FOR KIDS :
COMMODORE 64
TABLE OF CONTENTS
PREFACE VI
CHAPTER 1: INTRODUCTION 1
What This Bookis About , 1
Who This Book is For 2
Why Use Assembly Language 3
Whatis An Assembler? 5
Machine and Assembly Language 6
Assemblers Covered in this Book 8
The Commodore 64 Macro Assembler
Development System 8
Merlin Assembler 8
The Kids' Assembler (Included in the book) 9
Other Assemblers not Covered 9
BASIC and Assembly Language 10
Machine Subroutines from BASIC 13
Setting Up 17
CHAPTER2: USING AN ASSEMBLER 21
ASSEMBLERS IN GENERAL 21
The Standard Parts of An Assembler 23
I.
Standard Editor/Assembler Format 27
USING THE KIDS' ASSEMBLER 29
Creating and Saving Programs 43
Special Conventions in Opcodes 47
Loading and Executing Programs 48
Some Examples 50
CHAPTER 3: THE MERLIN 64 ASSEMBLER 53
Using the Editor/ Assembler 53
Loading and Running Programs 67
THESOURCEROR 68
MERLIN'S MONITOR 70
Some Examples 70
CHAPTER 4: THE COMMODORE 64 MACRO ASSEMBLER
DEVELOPMENT SYSTEM 73
THEPARTS 73
EDITOR64 74
Added Editing Functions on EDITOR64 79
ASSEMBLERS 80
LOADERS 82
THEMONITORS 83
SomeExamples 88
CHAPTER5: STRANGE NEW NUMBERS 91
Decimal, Binary and Hexadecimal Numbers 91
Going Between Number Systems 96
Using Conversion Charts 96
Conversion Programs 98
How Not To Worry About Numbers 101
CHAPTER 6: WHAT'S IN YOUR MICROPROCESSOR? . 103
What's a Register? 103
The Accumulator 104
The X and Y registers 105
. The Processor Status Register (Status Register) 105
NegativeFlag 106
overflow Flag 107
BreakFlag 107
DecimalFlag 107
II.
InterruptFlag 107
Zero Flag 107
CarryFlag 108
StackPointer 108
Program Counter , , 110
Input/Output Port 110
CHAPTER7: MEMORY AND STORAGE 113
Line numbers and Addresses : A Comparison 113
ROM and RAM memory ..;..' 116
MINI-MONITOR 119
Backward Numbers : Low-Byte /High-Byte Storage 121
Bytes, Opcodes and Addressing Modes 123
SUMMARY 124
CHAPTER8: JUMPING IN 125
Where to Stick Your Programs 125
Auto-Placement With Kids' Assembler 125
ORG Pseudo-Opcode In Merlin . . 125
Commodore Assembler's * = function 126
Visiting Built-in Subroutines with JSR 126
Getting Out with RTS 128
Loading up with LDA 129
Implied, Immediate and Absolute Addressing Modes ... 1 32
Storing with STA 134
Storage in Empty, Unused RAM 134
Storage in "Soft-Switch" Addresses 135
Storageon Your Screen 140
SUMMARY 142
CHAPTER 9: USING THE X AND Y REGISTERS 143
How to use the X and Y Registers 143
Transfers With TAX, TAY, TXA and TYA 145
Incrementing and Decrementing with
INX,INY,DEXandDEY 148
From X and Y to Memory with STX and STY 150
Addressing Modes with the X and Y Registers 153
Indexed Absolute Addressing 154
Indexed Indirect Addressing 157
Indirect Indexed Addressing 161
III.
CHAPTER 10: LOOPS AND BRANCHES 165
Program Structure 165
Sequential 165
Loops 166
Branches 166
How BASIC Logic Works in Assembly Language 167
Looping to Save Programming Time 168
Indexing With Loops 172
NestedLoops 175
Branching Forward With JMP, BEQ and BNE 177
SUMMARY 181
CHAPTER 11: ADDING AND SUBTRACTING 183
Incrementing and Decrementing Memory : INC and DEC . . 1 83
Adding and Subtracting in the In the Accumulator:
ADCandSBC 186
Using CLC and SEC 187
SUMMARY 191
CHAPTER 12: INTERACTING WITH ASSEMBLY
LANGUAGE PROGRAMS 193
Introduction ; 193
Reading Input From the Keyboard 194
Joystick Control 203
The EOR instruction 205
Making Messages: ASC and .BYTE 221
Message Maker for Kids' Assembler 215
SUMMARY 218
CHAPTER 13: HOT GRAPHICS 218
Introduction 219
Low Resolution Graphics 220
Saving Plotted Lines 228
Animation 230
External Control of Movement 237
SUMMARY 246
CHAPTER 14: BLAZING SPRITES AND
MONSTROUS SOUNDS 247
Sprite Graphics 247
Sprite Creation 251
IV.
Sprite Building 260
Kids' Sprite Assembler 265
Full Horizontal Movement 272
Sprite Expansion 272
Assembly Sounds 274
SUMMARY 278
CHAPTER 15: DOWN THE ROAD 279
Introduction 279
Merging Subroutines 280
Appending With Merlin 280
Appending With Commodore EDITOR64 280
Appending and Inserting Subroutines 285
Getting to Know the Other Opcodes 289
Resources for Learning more About
Assembly Language Programming
on the Commodore 64 290
UserGroups 290
Reference Books 291
How-To Books 292
Magazines 293
You're On Your Own 294
APPENDICES 297
APPENDIX A: KIDS' ASSEMBLER 299
APPENDIX B: 6510 OPCODES 309
APPENDIX C: MEMORY MAP 1: DIAGRAM 315
APPENDIXD: MEMORY MAP 1: PLACES TO VISIT. . 317
APPENDIX E: BASIC TOKEN CHART 321
APPENDDC F: HEXADECIMAL-DECIMAL
CONVERSION CHART 323
APPENDIX G: DECIMAL-HEXADECIMAL
CONVERSION CHART 325
APPENDIX H: SCREEN STORAGE ADDRESS TABLE 327
APPENDIX I: COLOR STORAGE LOCATION TABLE 329
APPENDIX J: ASCII CODE 331
APPENDIX K: SCREEN STORAGE DISPLAY CODES . 333
INDEX 335
PREFACE
Learning assembly language programming is a challenge not
everyone will accept. It is not as easy to learn as BASIC, and it re-
quires learning how to work an assembler as well as learning
assembly language itself. Many who attempted to learn assembly
language have been stopped by lack of an assembler, lack of instruc-
tions on how to work an assembler or lack of instructions for learn-
ing assembly language on the Commodore 64. If any one problem
didn't prevent learning the language, the combination of problems
did.
Since these problems are real, I decided that it would be a good
idea to create a book that provided solutions to all of these hurdles.
First, the book would supply a simple assembler that would be in-
structional as well as functional. Secondly, it would explain how to
work the most popular assemblers for the Commodore 64, in-
cluding the one provided in the book. Finally, the book would take
assembly language a step at a time, explaining how to program using
assembly language instructions. That's what this book does.
As the title implies, this book was designed for kids. This doesn't
mean the book is for children, but rather it's for kids who want to
enjoy learning something. I would have never learned BASIC or
assembly language if at some point I didn't start enjoying myself.
The same principle applies here. If we try to have a good time while
learning assembly language, it becomes an interesting challenge in-
stead of boring work. There are lots of examples that show
something about assembly language in a fun way.
VI.
ACKNOWLEDGEMENTS
If ever there were a group of people who were willing to help an
author, it's my Commodore computer club, the San Diego Pet
Users Group. Jane Campbell, Don Johnson and Barbara Prouty
answered plenty of questions and provided lots of ideas. Fellow
authors, Guy Grotke, who wrote Intermediate Commodore 64, and
David Miller, author of Commodore 64 Files, were equally helpful
with information and suggestions. Eric Goez is responsible for
teaching me most of what I know about assembly language pro-
gramming. Roger Wagner, author of Assembly Lines: The Book,
served as an excellent editor. My own kids, Billy and David, were
guinea pigs whenever I could detach them from MTV. My wife Eli,
as usual, was a good sport about the whole thing. Naturally, any
fault with this book lies with the author, not with those who so will-
ingly helped.
VII.
CHAPTER 1
INTRODUCTION
What This Book Is About
This book is about creating machine language programs with an
assembler. So the first thing we will learn how to do is to use an
assembler. If you do not have an assembler, I've included one in this
book you can use. You will learn how to write assembly language
with specific assemblers. We will go through their use step by step
covering only their basic features. Most assemblers have advanced
features you can use, but we will not be going into these aspects of
assemblers. Once you are comfortable on this level, you can read
more advanced books and documentation for special features of the
various assemblers. If you already have an assembler, and you know
how to use it, you can skip Chapters 2-4 since their sole purpose is to
explain how to operate different assemblers.
Secondly, this book will explain using assembly language coding
procedures. There are many different operations you can perform
with an assembler. In fact there are 151 different machine opcodes
accessed by 56 assembly language opcodes. An opcode is an instruc-
tion something like BASIC statements. However, we are not going
to cover all the assembly language opcodes. Some opcodes involve
complicated operations, and we're not going to get overly advanced
1
in this book. It is more important to learn how to use the fundamen-
tal operations well and understand their use clearly than try and
learn everything at once and not understand what you're doing.
Nevertheless, when you are finished with this book, you will be able
to write assembly language programs. Furthermore, we are only go-
ing to deal with the Commodore 64. If you have a different com-
puter, even a VIC 20, you should get a different book.
Finally, we're going to have to spend some time on how your
microprocessor handles code, different number systems and
storage. To be honest, this isn't a lot of fun, but once we're through,
you'll understand a lot more about your computer and how to han-
dle machine and assembly language operations. This is all covered in
Chapters 5-7. If you know about binary, hexadecimal and decimal
number conversions and how the whole thing works in your
microprocessor, skip these chapters. (There's no sense in spending
time on something you already understand.) From Chapter 8 on, we
start writing assembly language programs.
Who This Book is For
This book is for kids who want to begin programming in
assembly language. You might want to know the difference between
a general book for beginners and one for kids. In a lot of ways there
is no difference, especially if this is your first venture into assembly
language. However, we're not going to get too serious, and the em-
phasis will be on having a good time learning assembly language
programming instead of a comprehensive guide to professional pro-
gramming. Also, we're going to play a lot with little routines that do
crazy things to your Commodore 64 instead of writing programs for
the sequential development of applications. This means we're not
going to be getting into abstract structures and theories about
microprocessors. Instead the emphasis will be on learning by doing.
Most of the routines will display something to the screen so that you
can see what's going on. This means we will be doing a lot with text
and graphic displays and not so much with mathematical manipula-
tions important for business applications. Besides, its more enter-
taining to watch a sprite go screaming across the screen than adding
up a column of numbers.
As a warning to those of you who just got your Commodore 64,
you should know that assembly language programming is a lot more
difficult to master than BASIC. In fact, if you do not know how to
program in BASIC, I would strongly recommend you learn it before
tackling machine and assembly language. Many of the examples
and explanations are based on the assumption that you have a work-
ing knowledge of BASIC. There are several good books for kids
learning BASIC, including KIDS TO KIDS ON THE COMMO-
DORE 64 and KIDS AND THE COMMODORE 64, and you
should read them before going on here. However, if you know how
to program in BASIC, you're halfway to understanding machine
language since you can transfer much of the BASIC programming
logic to assembly language programming.
Finally, since this book is designed for kids, you might expect to
be talked to as adults tend to talk to kids. The last thing I want to do
is to talk down to anyone. We'll go slowly and clearly, and we'll
have some fun, but don't expect to be treated like a half-wit. Also,
this book is not going to have a lot of tests to see if you got
everything right. Books with little tests at the end of the chapters are
fine if you like taking tests. You have enough tests in school; so you
won't get any in this book. Instead we'll have lots of example pro-
grams that explain and illustrate the use of various assembly
language programming techniques. By using your own imagination
and playing around with the various opcodes, you can learn more
than by taking tests.
Why Use Assembly Language?
Most books on assembly and machine language explain the speed
and compactness of machine code as the major reason to use it in-
stead of BASIC. That's certainly true, but I like assembly language
because you can really grab control of your Commodore 64, im-
press your friends and make a fortune. If you have ever played a
good arcade game, chances are it was written in machine language
with an assembler. The sound and speed of movement just aren't
possible with BASIC. If you ever shelled out money for such a
game, who do you think is getting it? A good chunk of the money
goes to the person who wrote the game program. Believe it not, I
know kids who can retire when they graduate from high school with
the money they've made programming with machine language.
Several other kids I know have part-time jobs after school working
for businesses requiring customized programs written with
assemblers. They may not make a fortune, but they get paid better
than the kids working in fast food joints and enjoy their work a heck
of a lot more. Now don't expect to write a best-selling arcade game
after you read this book or even get a paying job as a programmer,
but you will get started, and even the best programmers had to start
somewhere.
Now if you're not interested in making money as a professional
programmer, you can treat assembly language programming as an
adventure game. The whole purpose of adventure games is to find a
treasure hidden in a maze of caverns, tunnels, castles and dungeons.
Believe me, once you enter the caverns of your Commodore 64's
memory with its 64,000 caves of RAM plus all the tunnels of ROM,
you'll need all your wits to save your neck. What's more, once you
understand the memory, you will have all kinds of control even the
best adventure games cannot provide.
Finally, if you have any
friends who know even a little
assembly language programm-
ing, you got to admit that you're
impressed. Kids who can handle
machine code have a certain
magic about them that draws
admiration. I admit it; it's fun to
impress people and show off.
You don't need any brass band
or be a loud mouth with
assembly language. Just crank
up some code and let her rip.
Assembly language
programmers
have style!
4
This book is not meant to disparage BASIC; I use BASIC all the
time. It's quicker to write a program in BASIC and less difficult to
de-bug. As we will see later in this chapter, one of the most useful
applications of assembly language programs is to write subroutines
for BASIC. So instead of abandoning BASIC altogether, you can
use assembly language programs to enhance your BASIC programs.
What is an Assembler?
An assembler is a program that allows you to enter machine
language using a standard set of "mnemonics." What?!! First we
better explain what "mnemonics" are. Pronounced 'ni-mon-iks',
mnemonics are aids to help remember something. All of the instruc-
tions for your 6510 microprocessor are given with three letter
mnemonics. For example, one commonly used instruction is
'LDA.' The mnemonic LDA stands for LoaD Accumulator. The
machine language opcode for LDA in the immediate mode is the
hexadecimal number $A9 or decimal 169. Since it's a lot easier to
remember LDA instead of $A9 or 169 when you want to LoaD Ac-
cumulator, programmers prefer using assemblers instead of enter-
ing machine code directly. (In fact, a lot of machine code is entered
with POKEs from BASIC. A POKE X.169, where 'X' is some ad-
dress in memory, could very well be a machine language instruction
from BASIC to perform an LDA.)
Secondly, assemblers keep track of everything for you. When
machine language is entered into your machine, you must use se-
quential addresses for your instructions and values. Let's suppose
you want to enter your machine language program in addresses
49152 to 49162 ($C000 - $C00A). Depending on what instructions,
modes, values and addresses are used, more or less memory will be
taken up. Instead of having to figure out all of these elements as you
go along, the assembler does it for you. For example, if you use the
LDA instruction in the immediate mode, you take up two addresses.
However, if you use LDA in the absolute mode, you need three ad-
dresses. Furthermore, while LDA in the immediate mode has a
machine language value of $A9 (169 decimal), LDA in the absolute
mode has a value of SAD (173 decimal.) This is all understood by
your assembler, and you don't have to do anything other than give it
the mnemonic instruction and mode to have it do what its supposed
to do.
If you do not understand everything just discussed, DON'T
WORRY! Essentially, all that I'm trying to tell you is that an
assembler makes programming in machine language a lot easier!
You're not expected to grasp everything all at once, and what is
unclear at this point will make a lot more sense as you start working
with an assembler. The trick is to forge ahead and start working with
an assembler and the programs and instructions in this book. You
will remember when you started learning BASIC that everything
was not clear until you started experimenting, learning special tricks
and writing your own programs. The same is true with assembly
language. Practice will lead to understanding.
Machine and Assembly Language
By now, you probably realize that assembly and machine
language are two sides of the same coin. Assembly language uses
mnemonic codes to enter numeric machine code. In addition, an
assembler keeps track of everything needed for a machine program
to execute. For example, let's look at three programs that all do the
same thing. First, we'll look at how the program would be entered
with an assembler, then we'll see what it looks in machine language,
and then we'll see how it would be entered from BASIC directly into
machine code. Each method ends up with the same result, and I'll let
you decide what method is the most understandable and least dif-
ficult. All the program does is to jump to a subroutine located inside
your computer at hexadecimal address $E544 (58692 decimal) and
then returns to BASIC. The subroutine clears your screen and puts
the cursor into the upper left hand corner.
Assembly Language
ORG $C000 ; Begin storing program here
JSR $E544 ; Jump to subroutine at $E544
RTS ; Return to BASIC
Machine Language
$C000 $20
$C001 $44
$C002 $E5
$C003 $60
BASIC
10 C = 58692 : REM DECIMAL VALUE FOR $E544
20 LB = C - INT(C/256) * 256 : REM LO-BYTE
30 HB = INT(C/256) : REM HI-BYTE
40 POKE 49152,32 : REM JSR
50 POKE 491 53, LB
60 POKE 49154.HB
70 POKE 49155,96 : REM RTS
If you think the assembly language program is the simplest you're
right! Even with the REM statements, it's difficult to know what's
going on in the BASIC program, and at this stage of the game, the
machine listing should make no sense at all! In the assembly pro-
gram, all you have to do is to tell it where to start placing the code
with the ORG directive (or some similar instruction, depending on
the assembler), and then using the mnemonic codes, tell it what you
want it to do. Since the program is very small, using only four ad-
dresses or bytes, you can imagine how difficult it would be to enter
everything in machine language or POKE it in with BASIC with
longer programs. In magazine listings, you've probably seen BASIC
listings with POKEs and then a mile of DATA statements with the
values to be POKEd in. The programmer probably had to first write
the program using an assembler and then "disassemble" it with
PEEKs to get all those values.
Just for fun, key in the BASIC program and RUN it. When
you're finished enter:
SYS 49152
Your screen will clear. Of course it's a lot easier to PRINT
"{CLR/HOME}" from BASIC to do the same thing, but a pro-
gram line in BASIC with the instruction to clear the screen and home
the cursor takes up a lot more room in your computer's memory.
Later in this chapter we'll compare the amount of memory used to
see precisely how much more space is used by BASIC and why
machine code is so much faster.
ASSEMBLERS COVERED IN THIS BOOK
Since learning how to use a specific assembler requires special in-
structions, I decided to go into detail on the operations of certain
ones that you may already have, are recommended or, if you don't
have one, the one in this book. If you have an assembler and know
how to use it, you can skip this section and Chapters 2-4. If, on the
other hand, you bought an assembler not covered in this book but
cannot figure out how to work it, take a look at Chapters 2-4, and
your's may work in a similar manner to ones we discuss. If all else
fails, you can always key in the Kids' Assembler in the book and use
it.
The Commodore 64 Macro Assembler Development System
Chances are if you own an assembler already, you probably have
the one made by Commodore. I found it to be difficult to under-
stand, but once I got used to it, it works fine. You can edit, assemble
and load your code. It also comes with a machine language monitor
for entering assembly or machine language routines. However, it
does take a lot of work to load all the different files necessary to do
the different tasks required in writing assembly programs. In
Chapter 4, we'll go through its use step by step so that you can use it
effectively.
The Merlin 64 Assembler
This assembler is my own personal choice, and if you don't have
an assembler and want to get a good quality one inexpensively, I
would suggest this one. Roger Wagner Publishing has made a
special deal for kids. In the back of this book, you will find a coupon
for the Merlin 64 Assembler at a reduced cost. You can also find it in
your local stores.
8
The Kids' Assembler
In Chapter 2 and Appendix A there are BASIC listings for an
assembler written especially for kids. (In Chapter 2, we go through
the program in parts to see how it assembles code, and in the Appen-
dix, the listing contains all of the instructions used by the 6510
microprocessor. For the time being, use the one in Chapter 2 since it
deals with just those instructions we will be using.) Since I wrote this
assembler, I can criticize it. It has a minimal editor, it uses non-
standard opcodes, and because it is written in BASIC it is slow.
However, it does have a certain amount of error trapping so that if
you enter an illegal value or instruction, it will let you know right
away. It also shows you where your code is going in decimal values.
Furthermore, since you will key it in yourself, it will help you
understand what's going on. Best of all, it's very simple to use, and
it's free! Sooner or later, though, you will want to get a good
assembler with an editor and monitor.
Other Assemblers Not Covered in this Book
There are several magazines that provide listings of machine
language and BASIC assemblers. Magazines such as COMPUTE! 's
GAZETTE and THE COMMANDER have had listings of
assemblers, editors and monitors for the Commodore 64. Each
assembler, like the ones discussed in this book, have their own uni-
que characteristics and manner of handling assembler instructions.
Likewise, several companies make commercial assembler/editors,
but there are far too many to explain each one's use here. Since the
documentation for these assemblers ranges from the fairly clear to
the almost incomprehensible, you might have a problem under-
standing how to use them. Therefore, I have provided a short sec-
tion at the beginning of Chapter 2 entitled ' 'Assemblers in General' '
to give you a running start on their use. Actually, we will be dealing
with entering code with "editors" in that section, but most
assemblers have built-in editors so that when you run the assembler,
you also have access to the editor. (The Commodore Editor64 and
Assembler64 are separate co-resident files, however.) After reading
that section, re-read the documentation with your assembler/editor
and you can probably get everything going.
BASIC and Assembly Language
As we have seen, there are some connections between BASIC and
assembly language. In fact, after going through a number of inter-
pretive steps, the programs you write in BASIC are ultimately ex-
ecuted in machine language. Since there are so many interpreta-
tions, though, it takes longer to execute a BASIC program than one
written in assembly language. Let's take a look at our little machine
language program to clear the screen and one written in BASIC that
does the same thing.
JSR $E544
RTS
10 PRINT CHR$(147) ^Leave this in memory
We used only four addresses for our machine language program.
Address Opcode or Operand
Hex Dec
$C000 49152 $20 ■* Machine code for JSR
$C001 491 53 $44 -* Low byte of jump address
$C002 49154 $E5 ^High byte of jump address
$C004 491 55 $60 ^Machine code for RTS
Now, how do we examine the memory used by our BASIC pro-
gram? The most simple way is to subtract the amount of free
memory we have from the 3891 1 bytes that are free when we start up
the Commodore 64. You remember the FRE(0) statement in
BASIC. If you PRINT FRE(0) you will be presented with the
amount of available memory. Since this is a negative number, we
have to add it to 65536 to see how much memory is available. Then
by subtracting that amount from 3891 1 we will find how much our
one line program used. Therefore enter:
PRINT 38911 - (65536+ FRE(0)) {RETURN}
You should have gotten '17', the number of bytes used by the pro-
gram. That's over four times the amount used by our assembly
language program!
10
To actually see the code in your computer in a BASIC program,
we will "disassemble" the BASIC code. In your Commodore 64,
BASIC programs begin at location $800 (2048 decimal). To find the
end of the BASIC program and the start of variables, we look at a
'pointer' at locations $2D-$2E (45-46 decimal). A pointer is pretty
much what it implies - it points to an address. By PEEKing at the
locations between 2048 and the address stored in the pointer to the
end of the program, we should be able to see the entire BASIC pro-
gram. So first, we have to find the end of the program. We do that
by PEEKing the value stored in locations 45-46 using the following
algorithm:
PRINT PEEK(45) + PEEK(46) * 256 {RETURN}
= = EVERYTHING IS BACKWARDS! = =
Now if you're as smart as you look, you're probably wonder-
ing why the heck we used that weird second PEEK. Why did we
have to multiply it by 256 instead of just a regular PEEK? If
you look at the machine code listing, you will see that when we
jumped to the subroutine at $E544, the code was entered as $44
$E5 in two consecutive addresses. That's called low-byte, high-
byte storage. Therefore, when we pull a number out of two
consecutive storage address, we convert it to the correct
decimal value by multiplying the high byte (the second address)
by 256. Then by adding the high and low bytes, we get the cor-
rect decimal number. You don't have to worry about
understanding all of this now, but just remember that numbers
are stored backwards! In Chapter 3, we'll explain more about
this feature of your Commodore 64's memory.
You should have gotten '2064'. After all, we know that the BASIC
program used 17 bytes of memory and BASIC programs begin at
2048. Thus, 2048 = 17 - 1 = 2064. (Since the program uses ad-
dresses 2048 to 2064 inclusive, we subtract 1 from used memory to
get 2064.) Now, by PEEKing locations from 2048 to 2064 we can
see our entire program. Enter the following:
11
FOR X = 2048 TO 2064 : PRINT X;PEEK(X) : NEXT X
{RETURN}
You get the following "disassembly":
Byte# Address
1 2048 ^Beginning of BASIC
2 2049 14
3 2050 8
4 2051 10 -*l_ine number
5 2052
6 2053 1 53 ^ PRI NT statement
7 2054 32 ^Space
8 2055 199 ^CHR$ function
9 2056 40 -*Left parenthesis
10 2057 49
1 1 2058 52 m Mystery numbers
12 2059 55
13 2060 41 ** Right parenthesis
14 2061
15 2062
16 2063
17 2064 88 ^Beginning of variables
Just look at all of that to clear the screen! When you key in a
BASIC program, you automatically key in a machine language pro-
gram as well. While it is less work for you to key in BASIC, it's a lot
more work for your computer as you can see.
When you key in a BASIC word, it is 'tokenized' into a machine
code value. For example, we can see the token for PRINT to be 153.
Likewise CHR$ is 199. The line number is there along with the left
and right parenthesis, the space and some "mystery numbers" that
are special codes for CHR$ values. (Appendix D has a complete
listing of the BASIC tokens.) In the next section we'll do some tricks
with these values that will give you power over BASIC that you
never realized you had.
At this point you should be convinced that machine code is more
compact than BASIC as far as your computer is concerned. Fur-
12
thermore, you should be able to deduct that if there is less code for
the computer to read, it can execute it faster. Finally, you should see
that when you write BASIC you are also writing a machine language
routine. Now that you know all of that, let's see how you can use lit-
tle machine code routines in your BASIC programs.
Machine Subroutines From BASIC
An old saying among programmers is that 10% of a program
takes 90% of the time. For example, if you have a sort routine in
your program, most of the time spent by your computer is sorting
the numbers or strings you enter. The actual sorting routine may on-
ly take a few lines, but it is the routine that takes up the majority of
your execution time. Since BASIC code can be entered very quickly
compared with assembly code, a lot of programmers write machine
language subroutines that can be called from a BASIC program
using the SYS statement. In this way they can insert time-consuming
routines, such as sorts, in machine code and speed up the slow part
of the program without having to write the whole program in
assembly language. What's more, you can put the machine code in
one part of memory that will not interfere with the memory being
used by BASIC. To see how this works, let's write a little machine
code routine to change the color of your screen. We'll stick that in
memory out of the way of BASIC and then call it from BASIC with
SYS.
You probably know that decimal address 53281 holds the code
for your background color. By POKEing 53281 with values between
0-15 you can change its color. In assembly language to do that, we
would store a value in that location.
LDA#0
STA 53281
RTS
The above routine loads the accumulator with the number 0, the
code for a black background, and sticks it in 53281. We'll put that
routine at addresses 49152-49157 with the following BASIC pro-
gram.
13
10 BC = 53281 : REM ADDRESS OF BACKGROUND
COLOR
20 LB = BC-INT(BC/256) * 256 : REM LOW BYTE
30 HB = INT(BC/256) : REM HIGH BYTE
40 POKE 49152,169 : REM LDA
50 POKE 49153,0 : REM BLACK COLOR CODE
60 POKE 49154,141 : REM STA
70 POKE 49155,LB : REM LOW BYTE ADRS
80 POKE 49156,HB : REM HIGH BYTE ADRS
90 POKE 49157,96 : REM RTS
Now RUN the program and
then enter NEW. The BASIC
program is no longer in memory
because you entered NEW. Just
to be sure you got rid of it, enter
LIST {RETURN}. (If you get a
listing, better enter NEW
{RETURN} again.) The NEW
command got rid of the BASIC
program beginning in memory
location 2048. But since you put
your machine code way up in
49152, your machine code
should still be there. Now enter
the following BASIC program
that will use the machine
language program.
BASIC and machine language
can run together.
10 PRINT CHR$(147)
20 INPUT "HIT RETURN TO SEE THE SCREEN GO
BLACK";A$
30 SYS 49152
40 PRINT "NOW IT'S BLACK!"
14
By the way, in case you didn't notice it, the BASIC program to
enter machine code was pretty long. With an assembler, you'd just
have the three lines to enter. So while BASIC in general is faster to
program, it takes a lot more to write assembly language with a
BASIC program than it does with an assembler.
Some Tricks to Get Started
To get off on the right foot, you should use your knowledge of
machine language to do something impressive. In order to see a part
of your new power, try the following.
10 GOTO 30
65535 PROGRAM WRITTEN BY ME
30 PRINT "TA DA!": END
You can't do it. Right? As soon as you enter 65535 as a line number
your Commodore 64 burps out a SYNTAX ERROR. Now try it
again.
10 GOTO 3©
20 PROGRAM WRITTEN BY ME
30 PRINT "TA DA!": END
Normally, you'd get a SYNTAX ERROR in Line 20 since it is in
an illegal format. However, Line 10 jumps to Line 30 so there's no
problem. Now, let's PEEK at locations 43-44 to find the beginning
of the program, and list a screen of locations and their values.
PRINT PEEK(43) + PEEK(44) * 256 {RETURN}
We get 2049. (Actually BASIC programs begin at 2048, but the in-
teresting stuff starts at 2049.) Now let's "disassemble" the first 20
addresses of the BASIC program in memory.
FOR X = 2049 TO 2039 : PRI NT X; PEEK(X) : N EXT
{RETURN}
You'll see the following (assuming you entered the program exactly
as listed - even including the spaces).
15
2049 10
2050 8
2051 10 < Line number low byte
2052 <4Line number high byte
2053 137 ^GOTO token
2054 32 ^Space
2055 51
2056 48
2057
2058 36
2059 8
2060 20 -«Line number low byte
2061 <4Line number high byte
2062 80
2963 82
2964 79
etc...
Notice that address 2060 has the value of 20 for line number 20 .
Right after that is a zero (0). Notice also that following the line
number at address 2051, address 2052 has a zero (0). The zeros
represent the high byte value for any line number. Now suppose we
put the maximum value in the low byte and high byte addresses for
Line 20 . (The maximum value for any single byte is 255 or hex value
$FF.) Let's see what happens:
POKE 2060,255 : POKE 2061,255 {RETURN}
Go ahead and LIST your program. You now have the program with
the "illegal" line number. It should now look as follows:
10 GOTO 30
65535 PROGRAM WRITTEN BY ME
30 PRINT "TA DA!" : END
It will still RUN, but no matter how much you LIST it, it will not put
the line numbers in numeric order. Also, unless you re-POKE the
addresses storing the line number 65535, you can't get rid of line
65535. Go ahead and try. If you want to customize your programs
and include a line with your name on it that most BASIC program-
16
mers cannot delete, use that little trick. The following is another ver-
sion using the same trick. However, since we know the addresses
storing the second line number, we can include a line in the program
that will automatically change the line numbers.
10 GOTO 30
20 BY {YOUR NAME}
30 POKE 2060,255 : POKE 2061,255
40 PRINT CHR$(147) : LIST
When you RUN the program you screen will clear and you will see,
10 GOTO 30
65535 BY {YOUR NAME}
30 POKE 2060,255 : POKE 2061,255
40 PRINT CHR$(147): LIST
From the above, you should now be able to see that what is stored
in RAM space addresses can be changed. However, you have to
know something about how your BASIC or machine language pro-
gram is stored in order to make changes. If you can make those
changes, then you have taken the first step in machine language pro-
gramming. That's because the great bulk of assembly and machine
language prograrnrning is placing instructions and information into
addresses and using addresses to keep track of your program's in-
formation. Again, you probably don't understand everything at this
point, but by practicing the examples and reading the explanations,
things will soon come together. Also, by changing the examples and
experimenting on your own, you will begin to understand how your
computer works in your own terms. (Just for fun, why don't you see
if you can change the little machine program that turned the
background color of the screen to black. See if you can change it to
different colors using values between 1-15. They're all listed at the
end of this chapter.)
SETTING UP
Before you start using your assembler, be sure to make a back-up
copy of it on a separate diskette or tape. There are a million ways to
blow a disk or tape with machine code; so if you don't want to lose
17
your assembler, make a back-up copy of it. The two commercial
assemblers discussed in this book, Commodore Assembler and the
Merlin assembler, can be backed up. Be sure to put a copy of the
assembler on your work disk and keep the master disk in a safe
place.
Likewise, with the Kids' Assembler be sure to make a back-up of
the disk or tape you SAVE it on. If you don't feel like keying it in,
there's a coupon in the back of this book that you can send in and
get a copy on disk for $10. Whatever you do, though, make back-
ups of your assembler.
There are two versions of the Commodore 64 we'll classify as the
"old" and "new". In the old version, it is possible to store a
character in the screen memory area, and it will appear on your
screen after the screen has been cleared. On the newer versions, you
have to load both the color and character after a JSR $E544 (clear
the screen and home cursor) to see the characters. The old version
will work with either, but the new version requires the color.
Therefore, all of our programs will be set up for the newer version,
but will work on both the old and new versions.
If you have a monitor, the default colors on your Commodore are
not too clear. Therefore, both the Kids' Assembler and Merlin pre-
sent your screen with a white background and border and black let-
ters for better viewing. Since the default color of characters is white
after a JSR $E544, a white on white display is invisible. Therefore,
we will have to take care to keep the character colors straight.
If you have a color TV or color monitor and prefer the default
Commodore colors, or some other combination, you can change
them by POKEing the following addresses with values, represented
by the variable X, between 0-15.
POKE 53281.X -^Background Color
POKE 53280.X -* Border Color
X's value can be from 0-15
18
The colors are:
Black
4 Purple
8 Orange
12 Gray 1
1 White
5 Green
9 Brown
13 Light Green
2 Red
6 Blue
10 Light Red
14 Light Blue
3 Cyan
7 Yellow
11 Gray 1
15 Gray 3
This all may be familiar to you, but just in case you did not know
about changing your screen colors, you do now.
Now, let's get into assembly language!
19
20
CHAPTER 2
USING AN ASSEMBLER
Assemblers in General
When I encountered my first assembler, the greatest problem I
had was figuring out how to use it. There was no documentation for
it since it was a public domain version I got from my club. Later on
someone came along with the documentation, and after I read it, I
still didn't know how to work the darned thing!
Finally I realized the problem with most assemblers and their
documentation. THE DOCUMENTATION IS WRITTEN FOR
PEOPLE WHO ALREADY KNOW ASSEMBLY LANGUAGE!
For the kids (and adults) who do not know assembly language or
other assemblers, it doesn't help to know that a whiz bang assembler
has macro capabilities when you don't even know what a macro
capability is. Therefore, to get started, we're going to demystify
assemblers by exposing them for what they really are.
As we briefly mentioned in Chapter 1 , all an assembler really does
is to neatly and simply arrange machine language code. The code
with which we work in assembly language can be broken down into
three parts:
21
1. Addresses to store code.
2. Instructions to tell the
computer what to do next.
(Opcodes)
3. The exact location or
mode relative to the instruc-
tion. (Operands)
To understand a little about
what's going on with an
assembler, let's look at what
assembled (or compiled) code is
compared with the assembler in-
structions that made it get that
way. (You remember our com-
parisons between machine and
assembly code in Chapter 1.)
Compiler
Compiled Machine Code
HEX
DECIMAL
Address
Code
Address
Code
$C000
$20
49152
32
$C001
$44
49153
68
$C002
$E5
49154
229
$C003
$A9
49155
169
$C004
$A9
49155
8
$C005
$8D
49157
141
$C006
$21
49158
33
$C007
$D0
49159
208
$C008
$60
49160
96
Assembler Source Code
Line#
Opcode
Operand
1
JSR
$E544
2
LDA
#8
3
STA
$D021
4
RTS
22
We already know that assembly language is less mysterious than
machine code, but let me give you an idea of everything the
assembler did to compile the code.
1 . First, it looks at the opcode (the instruction) and the mode of
the instruction and decides how many bytes it will take. For ex-
ample, the instruction JSR (Jump to SubRoutine) takes up 3
bytes or addresses. The first byte is the machine opcode for
JSR and the next two bytes are needed for the address ($E544).
On the other hand, LDA in the immediate mode only needs
two bytes and addresses; one for the operand and one for the
value which has to be 255 ($FF) or less. The RTS opcode only
takes a single byte or address.
2. Next, the assembler arranges the operands greater than 255
($FF) and puts them in the lo-byte / high-byte configuration
your computer uses.
3. Finally, it places these values all in memory in ascending
order of addresses. ("Ascending" means from lower to higher
addresses.) This is called the assembled or compiled code.
The work of an assembler should not be very clear to you right
now, but later on the clouds will begin to clear and you'll say, "Oh
yeah! Now I get it." In the meantime, just think of an assembler as a
"code arranger" to make writing machine language simpler.
The Standard Parts of An Assembler
An assembler package sometimes has several different files
representing the different things an "assembler" does. Actually, the
"assembler" is only a single part of what most people call
"assemblers." Generally, we deal with "editors" and "assemblers"
as different parts of a single tool, but usually you never "see" the
assembler do its work. Instead, you will be working primarily with
the editor. Now let's take a look at all the parts:
EDITOR. The editor is used to enter what's called "source
code." All of the mnemonic instructions, such as LDA and
STA are accepted by the editor. When the source code is
23
assembled into machine code, it is called the "object code." In
simple editors, such as the one in the Kids' Assembler, you can
only enter source code. You can't edit it after its been entered.
(It does have a good deal of error trapping so that if you enter
illegal opcodes or operands, it will let you re-enter legal ones
before continuing.) On more sophisticated editors, you can in-
sert and edit lines, make global changes, move or copy big
chunks of code and save and load source code. The Merlin
Assembler and the Commodore 64 Assembler Development
System have editors that can do some or all of those things plus
more. When getting an "assembler", the real key to getting a
good one or bad one lies in what you can do with the editor, not
the assembler.
ASSEMBLER. Since the assembler part of an assembler is
pretty much invisible to the user, and they all do essentially the
same thing - compile code for you in machine language
- there's not a whole lot to say about how good or bad the ac-
tual assembler is. However, since assemblers are so closely link-
ed to the kind of editor one is using and how it handles code, in-
visible differences become apparent in the editor. A one-pass
assembler takes the opcodes and operands and orders them in-
to machine code either as soon as you enter the information in
the editor (as does the Kids Assembler) or when you finish your
program. A two-pass assembler is used in just about all com-
mercial assemblers for the Commodore 64. First, the two-pass
assembler finds the addresses and offsets for the labels, and
then on the second pass, compiles it into machine code. On the
Merlin Assembler and Commodore 64 Macro Assembler
Development System, you can use labels in your editor;
therefore, using two passes, the labels are automatically turned
into the correct addresses and offsets. On the Kids' Assembler,
since the editor will not accept labels, it compiles as soon as the
operand is entered. What this means for the user is that it's a
heck of a lot easier to use a two-pass assembler since you can do
more things simply with the editor.
24
ASSORTED OTHER PARTS
1. LOADERS and SAVERS. Some assemblers have the
loaders built into the editor while others have separate files for
loaders. Most commercial assemblers will load the source code
from the editor. Object code (the compiled code that runs) is
saved either as SEQ or PRG files on your disk. (Tapes only ac-
cept object code as SEQ files or as DATA statements in PRG
files.) If your object code is saved as a PRG file, you can load it
from disk with LOAD "FILENAME", 8,1 and then SYS its
beginning address to make it run. If saved as SEQ files, it is
necessary to have a driver program to first load the file into
memory and then SYS it. A good assembly package will allow
you to load and save source code and object code. It is especial-
ly important to have source code saved for developing larger
programs with an assembler so that you can work on the pro-
gram at different times. Likewise, it is far better to have your
object code saved as a PRG file so that you can execute it
without having to load a special loader program. The Kids'
Assembler is good in that it saves object code as PRG files, but
bad in that it saves the source code only as a SEQ file that can-
not be reloaded into the editor. (That's why the programs in
this book are relatively short!) The assembler by Commodore
is good at saving source code that can be reloaded into the
editor, but poor in that object code is saved as SEQ files requir-
ing separate loader programs. The Merlin Assembler not only
saves source code files, but it saves object code as PRG files.
Furthermore, using the Sourceror program that comes on the
Merlin Assembler disk, you can create source code from object
code stored as PRG files. (That may not mean much to you
now, but if you have a diskfull of object code saved with the
Kids' Assembler, BELIEVE ME, you'll someday want the
source code.)
2. MONITORS. Several assembler packages include monitors.
(Monitors, in this instance, do not refer to the special TV sets
for computers.) A monitor is a program that lets to examine,
enter, change and execute machine code. In Chapter 1 we used
a do-it-yourself BASIC monitor in examining the addresses
25
and code they contained. I didn't include a monitor in this
book since I'm going to let you write your own! (There'll be
plenty of tips on how to do it, though.) The Merlin and Com-
modore assembler packages contain very good monitors. The
Merlin monitor is especially useful since it is co-resident with
the editor/assembler.
3. DISASSEMBLERS. Disassemblers take assembled code in
memory and lists it in rudimentary mnemonic and machine
code. For example, a disassembly of our program in this
chapter might looks as follows: (All numbers are in hex-
adecimal values.)
C000 20 44 E5 JSR $E544
C003 A9 08 LDA #$08
C005 8D21D0 STA$D021
C008 60 RTS $60
Disassemblers are very useful to see the relationship between
machine code and assembly code. Take a look at the dis-
assembly above with the listings near the beginning of the
chapter to see if you can find the connection.
4. OTHER GOODIES. Assembler packages have all kinds of
enhancements to help you with your assembly code. Some have
de-buggers to help you find mistakes in your code. These vary
from the built-in error trapping in the Kids' Assembler to ones
that will let you find structural problems in your code. Second-
ly, source code generators are very helpful to examine how
others have created object code, even ones created with dif-
ferent assembler packages. The Sourceror in the Merlin
Assembler package is one such helpful program. Finally, many
assembler packages come with a set of source code files of
useful routines. These routines are not only valuable for seeing
how a certain operation works, but they can be incorporated
into your own programs to save you the time of developing
them yourself.
26
STANDARD EDITOR/ASSEMBLER FORMAT
When you first start entering assembly language code, you'll do it
from the editor. Just about all assemblers have four fields:
1. The label field.
2. The opcode field.
3. The operand field.
4. The comment field.
The fields arranged from left to right look like this in your editor:
LABEL OPCODE OPERAND COMMENT
As you enter the code in each field, to the left of the label field, line
numbers appear. These are something like BASIC line numbers, but
they usually have increments of 1 instead of 10 as in BASIC. (These
line numbers are not compiled!) A typical editor entry would look
liking the following:
LINE# LABEL OPCODE OPERAND COMMENT
1 LDX #$0 ; Load X register
with 0.
2 START TXA ; Transfer contents
of X to A
3 STA $400,X ; Store value in ad-
dress $400 indexed
byX
4 I NX ; Increment X
5 CMP #254 ; Compare A with
254
6 BNE START ; If A is not equal to
254 then go back
to line labeled
START
7 RTS
As you can see, it is unnecessary to always use all fields. Many
programmers do not use the comment field at all, while others use
27
several lines for nothing but comments. (Using the comment field is
equivalent to using REM statements in BASIC.) However, just
about every editor arranges its fields as they are above.
Editor/ Assembler program writers have an idea of how to best
put assembler packages together, and so there are differences that
creep in using one package or another. Perhaps the biggest is in the
use of "Pseudo-Opcodes." Pseudo-opcodes are instructions that
are never compiled into object code. Instead, they tell the editor to
do something with the code. Often, they are called "directives" to
differentiate them from opcode "instructions." For example, ORG
and EQU are two commonly used directives for defining the load
location of a program and defining addresses with labels. For exam-
ple the following is a common label definition you will see in listings:
HOME EQU $E544
and instead of keying in,
JSR $E544
the programmer can enter,
JSR HOME
If you plan to use an assembler package not discussed in the next
three chapters, keep in mind what we have discussed here. In that
way, if the documentation for your assembler is unclear, you can at
least expect to find the fields we discussed above. Go back over your
documentation and then go ahead to Chapter 8 and try out some of
the example programs. If you can get them assembled and running,
you can learn more about how to use your assembler as we go along
comparing what's in the book with your documentation. You might
also want to take a look at the way the Commodore assembler and
the Merlin Assembler assemblers are used to help you understand
your own.
28
USING THE KIDS' ASSEMBLER
First off, if you have either the Merlin Assembler or the Com-
modore 64 Macro Assembler Development System skip this
chapter, and go on to the chapters covering the assembler you have.
In fact, if you have any other assembler you know how to work, use
it instead of this one. However, if you have been confused by your
present assembler, the Kids' Assembler may help you get started.
Most importantly, by keying in this assembler, you'll learn about
what an assembler does. In Appendix A, there's a full blown version
of the Kids' Assembler. The one in this section has fewer opcodes
and it runs faster than the big one in Appendix A. I also included a
routine to save source code in this chapter. It doesn't save the source
code in a way you can re-use it, but you can read it. Also, I've
broken it down into sections so that you can do a piece at a time.
Once you key this in and get all the typing errors out, be sure and
make a back-up of it on a separate tape or disk! ! ! There are two dif-
ferent ending routines beginning in Line 760 . The first one is for disk
users and the second is for tape users. Use only one or the other. (If
you have a disk system and a cassette, just use the disk version.) By
the way, feel free to modify the program any way you want.
Okay, we're all set; so let's key in the first 15 lines and then see
what they do.
Kids' Assembler : C-64
10 POKE 53281,1 : POKE 53280,1 : PRINT CHR$(1 44)
20 GOSUB 4000
30X =
40 READ A : IF A = 255 THEN 60
50 READ B$ : READ C : X = X + 1 : GOTO 40
60 DIM DEC%(X),OPCODE$(X),BYTE%(X)
70 DIM AD(255),S$(255),C$(255)
80ER = X-1
90 RESTORE
100 FOR I = TO X-1 : READ DEC%(I) : READ
OPCODE$(l) : READ BYTE%(I)
110 NEXT I
29
120 PRINT CHR$(146);CHR$(147)
130 PRINT "ADRS";
TAB(10);"OPCODE";TAB(25);"OPERAND"
140 FOR X= 1 TO 40 : PRINT CHR$(114); : NEXT
150 PRINT
First the program sets the background and border colors to white
and the characters to black in Line 10 . This is simply to make it easy
for people using CRT monitors or black and white TV sets to see the
screen. If you like the blue colors, leave the line out or POKE in your
own colors. Line 20 goes to a header subroutine to give you
something to look at while the array is being loaded between Lines
40-110. The variable ER in line 80 is used in the ERror trapping
routine further on in the program. Line 30 just initializes the X
variable. (This is optional but a generally good habit.) Line 120
turns off the inverse mode and clears the screen. Finally, Lines
120-150 make your editor header showing the opcode and operand
fields along with the addresses where everything is going. It does not
have a label field, comment field or lines numbers. The line numbers
are replaced by the addresses to give you a clearer idea of what's go-
ing on with the assembler. The variables and arrays defined are:
X,l Counter Variables
A,B$ & C Data viewer variables
DEC%( ) Decimal value of machine opcode
OPCODE$( ) Nmemonic opcode
BYTE%( ) Number of bytes used by instruction
AD,S$,C$ Array variables for source code
This next block of code sets the starting address and asks for the
opcode.
Ififfl RFM ****************************
170 REM SET ADDRESS AND INPUT OPCODE
1810 RFM ****************************
190 SA = : PRINT "PRESS {RETURN} TO DEFAULT
TO 49152"
200 N =
30
210 INPUT "STARTING ADDR";SA : IF SA = THEN
SA = 49152
220BA = SA
230 PRINT SA;TAB(10)
240 INPUT OC$ : IF OC$ = "Q" THEN 760
250 C =
260 IF OG$ = OPCODE$(C) THEN D% = DEC%(C) :
B% = BYTE%(C) : GOTO 290
270C = C+1 : IF C>ER THEN PRINT
TAB(10);CHR$(18);"ERROR";CHR$(146) : GOTO 230
280 GOTO 260
290 IF B% = 1 THEN POKE SA,D% : SA = SA + 1
300 IF B% = 1 THEN S$(N) = OC$ : AD(N) = SA-1 :
N = N + 1 : GOTO 230
The first thing this block does is to set the starting address. It
defaults to 49152 ($C000) since that's a clear area of RAM. A lot of
programmers use the cassette buffer at 828 ($033C) since it is free
for disk users. Line 220 defines the constant BA to be the same as
SA (starting address) since SA is used as an address variable, and
we'll need the starting address later in the program. Lines 240 to 280
evaluate the opcode entered by the INPUT statement at Line 240 . It
searches the arrays for a match in line 260 and gets the decimal value
of the machine code and the number of bytes the instruction will
use. Line 290 sees if the opcode only uses one byte, and if it does
then it enters the machine opcode in the address and returns to line
230 for another opcode. Line 300 stores the source code informa-
tion. The variables are:
SA Variable address
BA Constant to store beginning address
OC$ Opcode entered
D% Decimal value of current opcode
B% Number of bytes used by current
opcode
C Counter variable
31
Now that we have the opcode entered, let's bring on the operand.
^1W REM *************
320 REM ENTER OPERAND
<VM| REM *************
340 PRINT TAB(25); : PRINT CHR$(145); : INPUT OPR$
350 AD(N) = SA : S$(N) = OC$ : C$(N) = OPR$ : N = N + 1
360 IF LEFT$(OPR$,1) < > "$" THEN
OPER = VAL(OPR$)
370 IF LEFT$<OPR$,1) = "$" THEN GOSUB 490
380 IF OPER > 65535 THEN GOSUB 630 : OPER = 0:
GOTO 340
390 IF OC$ = "BNE" OR OC$ = "BEQ" THEN GOSUB
700
400 IF OPER >255 AND B% < 3 THEN GOSUB 560:
OPER = 0: GOTO 340
410 IF OPER > 255 THEN GOSUB 640
First, in Line 340, the INPUT line is tabbed over and up to the
OPERAND field so you can see what you're supposed to enter.
Next, Lines 360-410 check out the operand for all sorts of condi-
tions:
360 If this is a decimal stick it in the variable OPER
370 If this is a hexadecimal number, go convert it
380 If this number is greater than 65535 go to the error
routine and have the programmer try again
390 If there's a conditional branch in the opcode go find
the branch offset
400 If the value is greater than 255 and it's only a 2 byte
opcode, go to the error routine and have the program-
mer try again.
410 If the value is over 255, go get it rearranged into the
lo-byte / high-byte values.
The new variables introduced are:
OPR$ Hex or decimal operand in string
OPER Numeric variable of operand
32
Once everything is all checked out and conversions are made after
the operand is entered, the code is immediately compiled in the next
routine.
AO0\ RFM ************
430 REM COMPILE CODE
440 REM ************
450 IF B% = 2 THEN POKE SA,D% : SA = SA + 1
460 IF B% = 2 THEN POKE SA.OPER : SA = SA + 1 :
OPER = 0: GOTO 230
470 POKE SA,D%: SA = SA + 1
480 POKE SA,LB : SA = SA + 1 : POKE SA.HB :
SA = SA + 1 : OPER = : GOTO 230
The compile block shows you just what's going on with an
assembler. It merely POKEs in machine code values for the
mnemonic opcodes and operands and keeps the addresses straight.
Lines 450-460 check for two byte opcodes, and then pops in their
values at the next two addresses, resets the opcode value, increments
the address, and then goes back to get the next opcode. Lines
470and 480 do the same thing for 3 byte opcodes using the LB flow
byte) and HB (high byte) variable supplied in a subroutine further
on in the program. (Line 410 in the previous block branched to the
subroutine for the LB and HB variables.) The new variables, LB
and HB will be discussed further on in the block where they are
defined.
At this point, go have a good stiff shot of root beer. (I did.) We've
actually gone through the entire assembly process! From this point
on, we will see the subroutines, DATA statements and ending block.
They're crucial to the program, but the heart of the program is
already keyed in.
Now we're ready to start on the subroutines. The first one is a
very good little one to make HEX-DECIMAL conversions in any
program. (HINT# $FF : If you want to write your own monitor, this
subroutine would be handy.)
33
490 REM **********************
500 REM CONVERT HEX TO DECIMAL
510 REM **********************
520H$=MID$(OPER$,2)
530 FOR L= 1 TO LEN(H$) : HD = ASC(MID$(H$,L,1))
540OPER = OPER*16+HD-48 + ((HD>57)*7)
550 NEXT L: RETURN
The heart of this subroutine is in Line 540. The loop between
530-550 converts the hexadecimal value in H$ to a decimal number
that can be POKEd into memory in the compile subroutine. Line
520 simply strips the T off OPER$ and stores the substrong in H$.
Next, we come to the double error trap for values over 255 ($FF)
entered with 2 byte opcodes and any operand value over 65535
($FFFF).
560 REM **********
570 REM ERROR TRAP
580 REM **********
590 PRINT CHR$(18);"ERROR-MUST BE LESS THAN
256"
600 FOR W = 1 TO 400 : NEXT W : PRINTCHR$(146); :
PRINT CHR$(1 45)
610 FOR X= 1 TO 27 : PRINTCHR$(32); : NEXT
620 PRINT CHR$(157);CHR$(157);CHR$(145) : RETURN
630 PRINT CHR$(18);"VALUE OVER 65535
($FFFF)";CHR$(146) : RETURN
Error Trap
34
There's nothing fancy about this subroutine. It simply pops an er-
ror message if you're over 255 (a little spiffy, I admit) and does the
same thing in a 1 line subroutine in 630 if your operand exceeds
65535.
Now the next subroutine is another one you could use in a
monitor program. It takes any number over 255 and splits it into
high and low bytes.
P\A0) RFM ************************
650 REM CONVERT TO 2 BYTE NUMBER
fififfj RFM ************************
670 LB = OPER-INT(OPER/256)*256
680 HB = OPER - INT(OPER/256)
690 RETURN
The routines in 670 and 680 do all the work. They're simple but
vital little formulas. See how these variables are compiled in Line
480. They created the following variables:
LB Low byte - first address
HB High byte - second address
Now this next subroutine determines the operand value by com-
paring two addresses. The value is the "branch offset" sending the
program branching forward or backwards.
70)0) RFM *************
710 REM BRANCH OFFSET
7?ff) RFM *************
730 IF SA > OPER THEN OPER= 254-(SA-OPER)
740 IF SA < OPER THEN OPER= (OPER-SA)-2
750 RETURN
Notice how the value has to be calculated differentiy depending
on whether the address value (SA) is greater or lesser than the branch
address (OPER).
At this point, take a good look at your system. If you use a
cassette to store your programs, skip this and go to the next block. If
35
you have a disk, this is the block you want. It gives you the options
of saving your program, ending or going back to the beginning. The
most valuable part of the routine is in Lines 890-970 . It contains the
save-to-disk-as-a-program routine for your OBJECT CODE! Lines
980-1040 save the source code as a SEQ file.
Disk Version Only
760 REM **************
770 REM ENDING ROUTINE
7R0 REM **************
790 NB = SA-BA
800 PRINT CHR$(147)
810 FOR X = 1 TO 5 : PRINT : NEXT
820 INPUT'SAVE PROGRAM(Y/N)";AN$
830 IF AN$ = "Y" THEN 890
840 PRINT : PRINT : PRINT "PROGRAM
IS";NB;"BYTES LONG"
850 PRINT "TO EXECUTE 'SYS'";BA : PRINT
860 INPUT "(B)EGIN AGAIN OR (E)ND";DE$
870IFDE$ = "B"THEN120
880 PRINT : PRINT'END" : END
890 PRINT CHR$(147) : FOR X= 1 TO 5 : PRINT :
NEXT
900 LB = BA-INT(BA/256)*256 : HB = INT(BA/256)
910 INPUT "ENTER FILE
NAME";NW$:NF$ = NW$:NF$ = "0:" + NF$ + STR
$(BA) + ",P,W"
920 OPEN2,8,2,NF$
930 PRINT#2,CHR$(LB) + CHR$(HB)
940 FOR X = BA TO SA-1: OC = PEEK(X)
950 PRINT#2,CHR$(OC)
960 NEXT X
970 CLOSE2
980NF$ = ""
990NF$ = "0:" + NW$ + ",S,W"
36
1000 OPEN 9,8,9,NF$
1010FORV = 0TON-1
1020 PRINT#9,AD(V),S$(V),C$(V)
1030 NEXT V
1040 CLOSE9
1050 GOTO 840
There's nothing fancy about the save, end or begin branches.
However, our constant, BA is used to find the number of bytes in
the program by subtracting it from SA, the last address entered + 1.
The save-to-disk routine from 890-970 is one I got from Guy
Grotke's book, The Intermediate Commodore 64. It stores the
beginning address of your machine language program as part of the
file in line 910. Therefore, when you LOAD "FILENAME" ,8,1
the program knows where to go. To help you remember that, I add-
ed the starting address to the file name. Therefore, when you save a
machine file, the name includes its load address and all you have to
do once the program is loaded is to SYS that address. The new
variables in this block are :
NB Number of bytes in program
X Counter variable
AN$, DE$ Strings for INPUT branches
NF$ Name of file to write to disk
OC Decimal value of opcodes and operands
If you just finished keying in the above block of the ENDING
ROUTINE jump (JMP in mnemonic opcode) to the block, OP-
CODE DATA. This following block is a repeat of the ENDING
ROUTINE except it is for saving a sequential file of your program
to tape instead of a PRG file to disk.
Tape Version Only
7fiffl RFM **************
770 REM ENDING ROUTINE
7Ml RFM **************
790 NB = SA-BA
800 PRINT CHR$(147)
37
810 FOR X = 1 TO 5 : PRINT : NEXT
820 INPUT'SAVE PROGRAM(Y/N)";AN$
830 IF AN$ = "Y" THEN 890
840 PRINT: PRINT: PRINT "PROGRAM IS";NB;"BYTES
LONG"
850 PRINT "TO EXECUTE 'SYS"';BA : PRINT
860 INPUT "(B)EGIN AGAIN OR (E)ND";DE$
870IFDE$ = "B"THEN120
880 PRINT : PRINT" END" : END
890 PRINT CHR$(147) : FOR X= 1 TO 5 : PRINT : NEXT
900 REM * * * TAPE SAVE * * *
910 INPUT "ENTER FILE NAME";NW$:NF$= NW$
920OPEN21,1,1,NF$
930 PRINT#21,BA
940 FOR X = BA TO SA-1 : OC = PEEK(X)
950 PRINT#21,OC
960 N EXT X
970 CLOSE21
980NF$ = ""
990NF$=NW$ + ".S"
1000 OPEN22,1,1,NF$
1010FORV = 0TON-1
1020 PRINT#22,AD(V),S$(V),C$(V)
1030 NEXT V
1040 CLOSE22
1050 GOTO 840
The good news is that you can save machine language programs
to tape, but the bad news is they are saved as SEQ files instead of
PRG files. That means we will have to have a special "Loader" pro-
gram for you tape users. We'll look at that later. For now, let's see
what the above block does. First of all, there are decision branches
in lines 820-880 . These just prompt you with questions about saving
your program and whether you want to end or start over. Line 790
finds the number of bytes (NB) in your program to be used both in
telling you how much memory you used and as a variable to be
stored on your tape file. Line 920 begins the storage sequence of
your program. Basically, this section OPENs a tape file, first stores
the number of bytes in your program (Line 930), and then it PEEKs
38
at your machine language program in memory and stores the values
on your tape. The loop in Lines 940-960 does this. The following
variables were introduced in this block:
NB Number of bytes in program
X Counter variable
AN$, DE$ Strings for INPUT branches
NF$ Name of file to write to tape
OC Decimal value of opcodes and operands
Now this next section will have to be done very carefully. It has all
of the information your assembler will use. Each DATA statement
uses a separate line containing the following information:
Decimal value of opcode
Mnemonic for opcode
Number of bytes used by opcode and mode
The reason I put every set of DATA statements in a separate line
was to help you debug typing errors and to see the relationship bet-
ween machine opcode (the first number), mnemonic opcode (the
string), and the number of bytes used by an operation (the second
number). Also, you will get a preview of the special conventions this
assembler uses in mnemonic opcodes. In most discussions of
machine and assembly language programming, the machine opcode
is given in hexadecimal values, but since your assembler is written in
BASIC, it needs the decimal values to 'compile' the code into
memory. OK, now take a deep breath and go ahead and key in this
code.
2000 REM ***********
2010 REM OPCODE DATA
2020 REM ***********
2030 DATA 24,CLC,1
2040 DATA 32,JSR,3
2050 DATA 56,SEC,1
2060 DATA 73,EOR#,2
2070DATA76,JMP,3
2080 DATA 77,EOR,3
2090 DATA 96,RTS,1
39
2100 DATA
2110 DATA
2120 DATA
2130 DATA
2140 DATA
2150 DATA
2160 DATA
2170 DATA
2180 DATA
2190 DATA
2200 DATA
2210 DATA
2220 DATA
2230 DATA
2240 DATA
2250 DATA
2260 DATA
2270 DATA
2280 DATA
2290 DATA
2300 DATA
2310 DATA
2320 DATA
2330 DATA
2340 DATA
2350 DATA
2360 DATA
2370 DATA
2380 DATA
2390 DATA
2400 DATA
2410 DATA
2420 DATA
2430 DATA
2440 DATA
2450 DATA
2460 DATA
2470 DATA
2480 DATA
2490 DATA
105,ADC#,2
108,(JMP),3
109,ADC,3
121,ADC-Y,3
125,ADC-X,3
129,(STA-X),2
133,STA-Z,2
134,STX-Z,2
136,DEY,1
138,TXA,1
140,STY,3
141,STA,3
142,STX,3
145,(STA-Y),2
148,STY-X,2
152,TYA,1
157,STA-X,3
153,STA-Y,3
154.TXS.1
160,LDY#,2
161,(LDA-X),2
162,LDX#,2
164,LDY-Z,2
165,LDA-Z,2
166,LDX-Z,2
168,TAY,1
169,LDA#,2
170.TAX.1
172.LDY.3
173,LDA,3
174,LDX,3
177,(LDA-Y),2
185.LDA-Y.3
186,TSX,1
188,LDA-Y,3
189,LDA-X,3
190,LDX-Y,3
192,CPY#,2
193,(CMP-X),2
196,CPY-Z,2
There's a
lot of DATA
in here!
40
2500 DATA 197,CMP-Z,2
2510 DATA 198,DEC-Z,2
2520 DATA 200.INY.1
2530 DATA 201, CMP#,2
2540 DATA 202,DEX,1
2550 DATA 204,CPY,3
2560 DATA 205,CMP,3
2570 DATA 26,DEC,3
2580 DATA 208,BNE,2
2590 DATA 221 ,CMP-X,3
2600 DATA 222,DEC-X,3
2610 DATA 224,CPX#,2
2620 DATA 230,INC-Z,2
2630 DATA 232,INX,1
2640 DATA 233,SBC#,2
2650 DATA 234,NOP,1
2660 DATA 236.CPX.3
2670 DATA 237,SBC,3
2680 DATA 238,INC,3
2690 DATA 240,BEQ,2
2700 DATA 249,SBC-Y,3
2710 DATA 253,SBC-X,3
2720 DATA 254,INC-X,3
27*30) REM ************************
2740 REM ADD ADDITIONAL DATA HERE
77^0) RFM ************************
2760 DATA 255
It is important to have line 2760 be the LAST DATA statement
entered. When your program READs 255 as a value, it knows that it
is at the end of the DATA. Therefore, if you get ambitious and add
additional opcode, move Line 2760 to the end of your new DATA
statements. In the full program in Appendix A, you will find all of
the DATA values for additional opcode. (Be careful in adding BCC,
BCS, BMI, BVC and BVS. When used, their operands will have to
be sent to the branch offset subroutine in beginning in line 700 . All
you have to do is to add lines like 390 to initiate the branch. Use line
numbers 391-399 for branch initiators with these branch opcodes
41
not included in this simplified version of the Kids' Assembler. The
full assembler in Appendix A takes care of all of this for you, of
course.)
Finally, we get to the commercial in this last block. The header
was included so that if you give a copy of the program to your
friend, he or she will know where to find the documentation on how
to use the assembler. If you ever had a program without the
documentation, you know how frustrating it is to use the program.
This is especially true with complex utilities like assemblers.
Therefore, this last block is important. (Also, it's a get-rich-quick
scheme to sell more books!)
4000 REM ******
4010 REM HEADER
4020 REM ******
4030 PRINT CHR$(1 47)
4040 CR$ = "(C) COPYRIGHT 1984" : NM$ = "BY
WILLIAM B. SANDERS"
4050 BK$ = "ASSEMBLY LANGUAGE FOR KIDS"
:CM$ = "COMMODORE 64"
4060 IS$ = "SEE" : F$ = "FOR DOCUMENTATION"
4070 H = 20-LEN(CR$)/2 : PRINT TAB(H);CR$
4080 H = 20-LEN(NM$)/2 : PRINT TAB(H);NM$
4090 PRINT: H = 20-LEN(IS$)/2 : PRINT TAB(H);IS$ :
PRINT
4100 H = 20-LEN(BK$)/2 : PRINT TAB(H);BK$
:H = 20-LEN(CM$)/2: PRINT TAB(H);CM$
4110 H = 20-LEN(NM$)/2 : PRINT TAB(H);NM$ : PRINT
4120 H = 20-LEN(F$)/2 : PRINT TAB(H);F$
4130 LD$ = "LOADING ARRAY" : FOR X= 1 TO 10 :
PRINT : NEXT : H = 20-LEN(LD$)/2
4140 PRINTTAB(H);CHR$(18);LD$
4150 RETURN
Phewwww! That sucker was long. Well, how many kids do you
know who wrote their own assemblers? If you did all that work, you
won't understand everything about assemblers and assembly
language, but you'll be ahead of those who haven't. Congratula-
tions! (Only real programmers write their own assemblers! ! !) Save
42
the program under the name "KIDS ASSEMBLER 1." We'll call
the big one in Appendix A, "KIDS ASSEMBLER 2."
CREATING AND SAVING PROGRAMS
ON THE KIDS' ASSEMBLER
Once you get all the typing errors out of your assembler, you're all
set to crank it up. REMEMBER to make a back-up copy or two on
separate disks or tapes. If you've done that, then LOAD "KIDS
ASSEMBLER 1" and then enter RUN.
The first thing you will see when you RUN the program is the
following header:
(C) COPYRIGHT 1984
BY WILLIAM B. SANDERS
SEE
ASSEMBLY LANGUAGE FOR KIDS:
COMMODORE 64
BY WILLIAM B. SANDERS
FOR DOCUMENTATION
{LOADING ARRAY}
The message stays there until the array is loaded and lets you
know something is happening. After a few seconds your editor will
pop up. It looks like the following:
ADRS OPCODE OPERAND
PRESS {RETURN} TO DEFAULT TO 49152
STARTING ADDR?
At this point you are expected to enter a starting address for your
program. If you just press the RETURN key, your program will
automatically begin compiling at 49152 ($C000). Generally, this
43
area of RAM is free for machine language programs and I like to use
it. If you do not use a cassette tape, the cassette buffer at 828 ($33Q
is also a good place to use. Later on we'll discuss the various place
where you can put your code. For now, just press the RETURN key.
As soon as you do, your screen looks like this:
ADRS OPCODE OPERAND
PRESS {RETURN} TO DEFAULT TO 49152
STARTING ADDR?
49152 ?
The prompt under the OPCODE field is waiting for you to enter an
opcode. (What else?) Enter JSR and press RETURN. Now your
screen looks like this:
ADRS OPCODE OPERAND
PRESS {RETURN} TO DEFAULT TO 49152
STARTING ADDR?
49152 ?JSR ?
The prompt has jumped to the OPERAND field awaiting an
operand. Now, you can either enter the operand as a decimal or
hexadecimal number. If you choose to enter a decimal number, just
key in the number. For hexadecimal numbers, though, first put in a
dollar sign ($) and then the number. To get started, enter the value
58692 or $E544 and press RETURN. Both numbers are the same
and have the same effect. The hexadecimal to decimal subroutine
automatically changes $E544 to the decimal value 58692. It's a good
habit to start thinking in terms of hexadecimal. After you've done
that, you screen will appears as:
ADRS OPCODE OPERAND
PRESS {RETURN} TO DEFAULT TO 49152
STARTING ADDR?
49152 ? JSR ? $E544
49155 ?
44
You've successfully entered a line of assembly code, and your
assembler is waiting for the next line. That's all there is to it! Some
opcodes have no operands, but for the most part, you just enter the
opcode and the operand. The last line of your code should be RTS
to return control of your computer to BASIC once the program is
executed with a SYS command. It's not a very powerful assembler,
but it's easy to use and will catch many common errors that begin-
ners make.
You may be wondering about the address field on the left side of
your screen. The first number was 49152 and the second is 49155.
Shouldn't it be 49153? What the assembler is doing is showing you
how much memory each opcode and operand is using. In the first
line you used three addresses or bytes. One byte was used for the op-
code and two bytes were used for the operand. Thus, addresses
49152, 49153 and 49154 have been used, and the next available ad-
dress is 49155. This will help you see where your code is actually go-
ing.
OK, let's end the program with RTS and RETURN. Now your
screen shows the following:
ADRS OPCODE OPERAND
PRESS {RETURN} TO DEFAULT TO 49152
STARTING ADDR?
49152 ? JSR ? $E544
49155 ? RTS
49156 ?
Since the RTS opcode used only a single byte and requires no
operand, you are now at address 49156. To end your work in the
editor enter 'Q' for 'quit' and press RETURN. You screen will clear
and you will be prompted with the following message:
SAVE PROGRAM(Y/N)?
Enter ' Y' and press RETURN and you will see the next message:
ENTER FILE NAME?
45
Enter the file name CS (for Clear Screen, since that's what the
program does) and press RETURN. Now you will see:
ENTER FILE NAME? CS
PROGRAM IS 4 BYTES LONG
TO EXECUTE «SYS' 49152
(B)EGIN AGAIN OR (E)ND?
If you press 'B' you will be immediately returned to the editor.
Since we've already seen the editor, press 'E' and RETURN to end
our little session with the Kids' Assembler. Your screen will look
like this now:
ENTER FILE NAME? CS
PROGRAM IS 4 BYTES LONG
TO EXECUTE 'SYS' 49152
(B)EGIN AGAIN OR (E)ND? {Press 'E'}
END
READY.
You're back in BASIC control of your computer. If you enter,
SYS 49152
and press RETURN your machine language program will execute.
Go ahead and do it to see what happens.
Now here's the really neat part about machine language. You
have two programs in memory at the same time. The assembler is
still in memory along with your little machine language program.
Just enter RUN {RETURN} to execute your assembler. Since your
BASIC program begins way down at 2048 ($800) and your
machine program is way up at 49152 ($C000), they won't conflict.
(Go tell your mother about that.)
46
SPECIAL CONVENTIONS IN OPCODES
Perhaps the biggest single problem with the Kids' Assembler is its
use of some non-standard opcodes notations. On the one hand,
these conventions were used to help you understand exactly what a
mnemonic opcode is in relationship to a machine language opcode.
On the other hand, it is a heck of a lot easier to write a relatively
short assembler in BASIC using the conventions I did! (Now you
know the awful truth.)
As we will see in Chapter 8, the 6510 has several different ad-
dressing modes. On most assemblers, the modes are determined in
the operand field. For example the following shows the instructions
for loading the accumulator with a 5 in the immediate mode and ab-
solute mode on most assemblers:
OPCODE OPERAND
LDA #5 -^-Immediate mode
LDA 5 ^-Absolute mode
The assembler can tell the first LDA instruction is in the immediate
mode since there is a pound sign (#) before the 5 in the operand
field. The second LDA instruction, however, is in the absolute
mode. The first LDA tells the computer to load the value 5 into the
accumulator. The second LDA, in the absolute mode, tells the com-
puter to load the value from address 5 into the accumulator. What
actually happens when the code is turned into machine language is
that the machine opcode for the first LDA is stored as $A9 (169
decimal) and the second LDA is stored as SAD (173) decimal. Since
the mnemonic opcode can be translated directly into a machine op-
code, by having the addressing mode as part of the mnemonic op-
code, you can better see the translation going on. Therefore, in the
Kids' Assembler, you put the addressing mode as part of your op-
code. Opcodes for the absolute, relative, and implied mode are ex-
actiy the same as on standard assemblers, but for other modes the
following conventions are used. (Don't worry about all the details
of addressing modes now. Later on in the book, we'll tackle each
one separately. Just take a look at the different conventions used.)
47
KIDS' ASSEMBLER
OPCODE OPERAND
LDA# 5 ■<- Immediate mode
LDA 5 «*- Absolute mode
As you can see, the absolute mode on the Kids' Assembler is the
same as the standard ones. However, the pound sign (#) has been
moved from the OPERAND field to the OPCODE field in the im-
mediate mode. The following is a full list of conventions you can
refer back to later when we cover the various addressing modes.
LDA Absolute mode (standard)
LDA# Immediate mode
TXA Implied (standard)
BNE Relative mode (standard)
LDA-Z Zero page mode
(J MP) Indirect
LDA-X Indexed
(LDA-X) Indexed indirect
(LDA-Y) Indirect indexed
Basically, the differences lay in where you put the special sym-
bols. I tried to make them consistent with the standard ones, and
simply place them with the opcode instead of the operand.
LOADING AND EXECUTING PROGRAMS
One of the best things about this assembler is the ease with which
you can load and execute machine language programs. From disk
all you do is to enter:
LOAD "PROGRAM NAME 49152",8,1
You then SYS the numeric value attached to the file name your pro-
gram will execute. The program is automatically loaded to the start
address from which you saved the program. Also it will not disturb
a BASIC program in memory. (Well almost, anyway.)
48
Getting your programs loaded from tape takes a special loader
program. That's because it's stored as a SEQ file and you have to
first read the file and then POKE the whole thing into memory.
(The best way to solve this problem is to buy a disk drive.) It's no
problem though with the following cassette machine language
loader program.
10 PRINT CHR$(147)
20 INPUT "NAME OF FILE TO LOAD "; NF$
30 INPUT "ADDRESS TO LOAD"; SA
40OPEN1,1,0,NF$
50 INPUT#1,NB
60 FOR X = SA TO SA + (NB-1)
70 INPUT#1,CD
80 POKE SA.CD
90 NEXT X
100 CLOSE1
Then just SYS the beginning address you entered when prompted
ADDRESS TO LOAD?
READING SOURCE FILES
Since a SEQ source file is saved with the object file in this version
of the Kids' Assembler, you will need a program to read your
source code. The first of the following two is for disk and the se-
cond is for tape:
SOURCE CODE READER DISK
10 PRINT CHR$(147)
20 INPUT "FILENAME ";NF$
30NF$ = "0:"+ NF$ +",S,R"
40 OPEN9,8,9,NF$
50 INPUT#9,A$
60 PRINT A$
70IFST = 0THEN50
80 CLOSE9
49
SOURCES CODE READER TAPE
10 PRINT CHR$(147):X =
20 INPUT "NAME OF SOURCE FILE ";NF$ :
NF$=NF$ + ".S"
30OPEN22,1,0,NF$
40 INPUT#22,A$,B$,C$
50 PRINT A$,B$,C$
60 A$ = "":B$ = "":C$ = ""
70IFST = 0THEN40
80 CLOSE22
The above programs will print your sources to the screen so that
you can see how you programmed your object code. It cannot, un-
fortunately, be loaded into your editor and reused.
SOME EXAMPLES
Before going on the Chapter 3, crank up your assembler for some
test runs. This will give you further checks on typos in your pro-
gram and show you that assembly language isn't impossible.
ERROR TESTS
ADRS OPCODE OPERAND
49152 ? XYX
{ERROR}
49152 ? LDA# ? 500 <• Enter on second try
{ERROR - MUST BE LESS THAN 256}
49154 ? LDA ? $FFFFA
{VALUE OVER 65535 ($FFFF)} ? 828
49157 ? Q
The above program just tested the error traps built into your
assembler. There's nothing worth saving; so just go back to the
beginning.
50
BACKGROUND, BORDER AND CHARACTER COLORS
ADRS
OPCODE
OPERAND
49152
?JSR
? $E544
49155
?LDA#
?0
49157
?STA
? $D021
49160
?LDA#
?4
49162
?STA
? $D020
49165
?LDA#
?5
49167
?JSR
? $E716
49170
?RTS
49171
?Q
When you SYS 49152 after you've exited the assembler, your
background will turn black, your border purple and your characters
white. If it worked, go on to Chapter 5. If it didn't, try it again and
make sure everything looks as it does above. If it still doesn't work,
check your BASIC assembler program, especially the DATA in the
opcode block.
51
52
CHAPTER 3
THE MERLIN 64 ASSEMBLER
Using the Editor/ Assembler
This is the best assembler I've seen for the Commodore 64. It
only works with a disk system; so if you have cassette storage, you'll
have to wait until you have a disk drive to use this one.
First of all, make a back-up copy of the Merlin Assembler and
put the original in a safe place. Using the back-up master, enter the
following:
LOAD "MERLIN",8
When it's loaded, enter RUN, press RETURN, and patiently wait
while the program is cranked up. (It takes a while.) Remove your
back-up master and put in your work disk. This should be a format-
ted disk that you can afford to destroy. It is possible to accidentally
write a machine code program that will cream your disk. (I do it all
the time.) In fact, it's a good idea to have two work disks; one to
keep in the drive while you're experimenting, and one on which to
save your source and object code.
53
The program starts in the "Executive Mode." You will see the
following on your screen:
{MERLIN}
By Glen Bredon
C :Catalog
L :Load source
S :Save source
A : Append file
R :Read text file
W :Write text file
D : Drive change
E :Enter ED/ASM
O :Save object code
G :Run program
X :Disk command
Q :Quit
Drive: 8
Source: $0A00,$0A0
%
From the Executive Mode, you can do a lot of house-keeping.
The first thing to do is to initialize your work disk.
Press X
The prompt will then show:
%Command:
Just enter "i" and press RETURN.
To see what's on the disk, press C for "Catalog." Your file direc-
tory will appear on the screen. There's nothing more to do in the
54
Executive Mode for the time being; so hit RETURN and enter the
Editor/ Assembler by pressing *E\ Your screen will look like this:
Editor
This is called the Command Mode. From here, you want to get
into the Add/Insert Mode. This is where you begin writing your
assembly language code. You can recognize it by the colon (:)
prompt. To get to the point where you can start writing assembly
programs, enter;
:A {RETURN}
As soon as you hit RETURN after entering the 'A' you're in the
"Add Mode." It is in this mode where you will be doing most of
your work. The number '1' will pop up, representing your first line
number.
Editor
:A
1
As in most assemblers, Merlin has four fields:
LABEL OPCODE OPERAND COMMENT
Each time you hit the space bar, you go to the next field. For the
most part, the OPCODE and OPERAND fields are the most im-
portant. When we get to branches, the LABEL field becomes very
important, and as we will see in later chapters, it is used extensively
for defining special locations and addresses as well. In our examples
in this chapter, we will show you how to use all four fields. To get
started, though, just try the following little program using the OP-
CODE and OPERAND fields:
LN#
LABEL
OPCODE
OPERAND COMMENT
1
JSR
$E544
2
RTS
55
To enter the routine, enter the Add Mode, and as soon a the T
appears do the following:
Step 1: Press the space bar to jump to the OPCODE
field and enter JSR.
Step 2: Press the space bar again to jump to the
OPERAND field and enter $E544. Now press the
RETURN key to go to the next line.
Step 3: When the '2' appears, press the space bar to
jump to the OPCODE field and enter RTS.
Step 4: Press RETURN twice. The first RETURN will
take you to the next line number, and the second
RETURN will take you out of the Add Mode and back in-
to the Command Mode. Pressing RETURN twice
without pressing any other key will always take you out
of the Add Mode.
The following shows how your screen should look now:
Editor
:A
1 JSR $E544
2 RTS
3^-
At this point, all the information you need for your assembly
language program is in the editor. In order to get in into a form you
can execute as a program, you must assemble it. To do that, from
the Command Mode, you enter,
:ASM
and press RETURN. You will be prompted,
:ASM
Update source (Y/N)?
You do not want to update the source code; so you press 'N', and
your code will be assembled. Your screen now looks as follows:
56
3^-
:ASM
Update source (Y/N)?
Assembling
8000: 20 44E5 1 JSR $E54
4
8003: 2 RTS
—End assembly, 4 bytes, Errors:
Symbol table - alphabetic order
Symbol table - numerical order
If that's what your screen looks like, you've just successfully
assembled your first program on Merlin. Next we will want to save
the program to disk, get out of Merlin, and then see if the program
runs. Before we do that, though, make a note of the first number in
the assembled code. It is the hexadecimal value $8000, shown on
your screen simply as 8000. This is the address where your object
code (your assembled machine language program) will load. It's
decimal value is 32768, and once your program is loaded, SYS
32768 will be used to execute it.
Now press 'Q' for quit and you will be returned to the Executive
Mode. The '%' prompt appears enter,
%S
Your screen will show:
%Save:
Be sure your work disk and not your master is in the drive and enter
a file name ('clear' is a good name since that's what the program
will do - clear your screen), and press RETURN. Your screen will
show:
%Save:clear
Saving clears
57
After saving the file, your screen will clear and you will be back in
the Executive Mode. Now, you have not saved anything that will
execute. You have just saved the source code. (Merlin put the ' .s' on
the end of your file name to distinguish it from the object code.)
This file has all the information you entered in the editor, but it did
not save the assembled code. To save what you assembled, called
the "object code," enter 'O' after the 'W prompt. (Make sure
that's an 'Oh' and not a 'Zero' .) When you do that your screen will
look like this:
%Object:clear
Since you just saved the source code for a program named 'clear',
Merlin assumed that the next file you want is an object code called
'clear' . You do and so just press RETURN without entering any file
name.
%Object:clear
Saving clear.o
Now you have saved both your source code and object code.
When you look at your disk's directory with the 'C command, you
will see a file named 'clear.s' and one named 'clear.o'. (Later when
you're not using Merlin and have your keys set for upper case, the
files will appear as 'CLEAR.S' and 'CLEAR.O'.)
Now, before we leave Merlin, take a look at the right side of your
screen. There you will see the addresses with your source and object
codes.
Source: $0A0,$0A10
Drive: 8 Object: $8000,$8004
In case you forgot to note the load address of your object code
after you assembled it, it is supplied here for you.
Let's get out of the Executive Mode and back to BASIC. Press
'Q' and when asked 'Do you really want to exit?', press 'Y' for
"Yes." Your screen will clear and you will be notified that to re-
enter Merlin use SYS 52000.
58
Once you're back in BASIC
you can execute your program.
When you entered ASM to
assemble it in Merlin, it was
placed in memory at location
$8000 (32768). Therefore, if
you SYS 32768 and press
RETURN, your program will
execute. Go ahead and try it. If
you did everything right, your
screen should clear. See how
easy that was?
EDITING YOUR SOURCE
CODE WITH MERLIN 64
To get back into the
editor/assembler, just enter,
Use the editor
to make changes
SYS 52000
and press RETURN. You'll be returned to the Executive Mode in
Merlin. Now, choose 'E' to enter the ED/ASSEM. As soon as
you're in the Command Mode indicated by the colon (:) prompt,
press 'L' and RETURN. Your source code should be there waiting
for you. The 'L' (L)isted your program; so now you know one of
the first editing commands. Whenever you want to see your source
code in memory, just press *L' in the Command Mode. Now, let's
take a look at the major editing commands. We'll start with a list of
the most important ones, and then show you how to use them.
EDITING COMMANDS
L Lists source code
A Enter editor at next available line number
l# Insert code into line number #
D# Delete line number #
E# Edit line number #
PORT# Selects printer as output port #
59
PRTR# Sends formatted listing to printer in
port#.
F Find a string in listing
C Change string
M Move lines of code
R Move lines in one range to another
section
NEW Clears source code from memory
L(ist). We've already seen how to list a source code with L.
However, when your programs get longer, you may want to stop
the listing to examine a certain section. Whenever you press the
space bar during a listing, you can step through the listing one line at
a time. To resume normal listing, press RETURN.
A(dd). When we first started writing our program, we pressed
'A' from the command mode and got a 1. With your program in
memory, press 'A' and you will be given the next available line
number. Having used only 2 lines, if we now press 'A' we'll start at
line 3.
I(nsert)#. This command is used to (I)nsert lines between lines.
With your 2 line program in memory, enter II {RETURN}. You
will now be in line 1. Press the space bar and enter the following:
ORG $C000
Press RETURN twice to get back to the Command Mode and press
L to list your program. Now it looks like this
1
ORG
$C000
2
JSR
$E544
3
RTS
The ORG instruction is a "pseudo-opcode." It is not assembled in-
to object code, but instead it identifies the beginning address for
your code. If you do not put an ORG in your program, it defaults
to the beginning address $8000 . It should be the first line of code in
your programs. As you learn programming in assembly language,
you will be leaving out a lot of code, and it will be necessary to insert
60
code between line numbers. Therefore, you will be using the (I)nsert
function a good deal.
D(elete)#. This is really simple to use. You just enter D and the
line or range of lines you want to get rid of and hit RETURN. Since
we don't have any lines in our program to delete, let's stick some
there to knock out. Do the following:
-v^-iffc
:A
4 0INK
5 BURP
6 CRASH
Deleting
unwanted code
After you enter line 6, hit RETURN twice to get back to the Com-
mand Mode. Lines 4, 5 and 6 are pretty worthless. List your pro-
gram and you'll see them all there at the end of your listing. Now
from the Command Mode, do the following:
: D6
Press RETURN and press L to list your program. Line 6 has been
(D)eleted. Now to delete a range of lines enter:
:D4,5
Hit RETURN twice and list your program again. All of those dumb
lines are gone.
E(dit)#. To change a line, press E and the line number to edit
from the command mode. Let's change our ORG from $C000 to
$033C. Do the following:
:E1 {RETURN}
1 ORG $C000
61
Press the space bar to jump to ORG and then using the right cursor
key, move over the ORG and hit the space bar again. The cursor
should be right on top of the dollar sign ($) of the $C000 . Move one
space to the right with the cursor key and replace $C000 with
$033C. Press RETURN and you're back in the Command Mode.
While in the edit mode you can (I)sert and (D)elete characters
with CTRL-I and CTRL-D. To try out these editing functions, let's
edit Line 2.
:E2
2 JSR $E544
Move the cursor over the 'S' in JSR and press CTRL-D. The 'S' will
be deleted. Now to get the 'S' back between the 'J' and the 'R' put
the cursor over the 'R' and press CTRL-I and enter 'S'. Experiment
with inserting and deleting code since you'll be doing a lot of editing
in assembly language programming. Here's a few more Edit Mode
CTRL commands to use:
CTRL-F Find a character
CTRL-0 Like CTRL-I except it inserts control character
CTRL-P This is really a neat command for putting a line
of asterisks across your screen. Use it for your header
blocks. If you hit the space bar and CTRL-P, you'll get
asterisks (*) on either side of your line.
STOP/RUN Gets out of the Edit mode without changing
the line you've edited.
CTRL-B Jumps to beginning of line
CTRL-N Jumps to end of line
CTRL-R Restores the line to its original state
CTRL-A Erases the line from the cursor to the right
PORT# PRTR#. If you have a printer hooked up to your
Commodore-64, you can send output to it using either the PORT or
PRTR commands.
If you use PORT, specify it as PORT 2, PORT 4 or PORT 5.
When you ASM your source code, you will be able to see all the in-
formation about it printed on paper. This is useful for debugging
62
programs. To get it going, from the Command Mode, enter the
following:
:PORT4
Press RETURN and your output will be vectored to your printer.
The PRTR command works almost the same as PORT but it for-
mats the output to your printer to include page breaks. Since you
really won't need this command until your programs are longer
(over 60 lines or so), you'll either have to enter a big program or
wait until you are more advanced. Here's an example format
:PRTR 4 "MAY 8, 1990" 1
The string is your page header and the number at the end is your
page number. The '4' refers to a channel to your printer port just
like with the PORT command.
F(ind). When you start having longer listing, this command is
really handy. From the command mode, you enter something like,
:F "JSR"
press RETURN, and all lines with JSR in it will be listed to the
screen. If you enter the range of lines before the string you're sear-
ching for, just that range will be listed.
:F 5,9"JSR"
C(hange). This command is good for wholesale mistakes. For ex-
ample let's say you have a program with JMP opcodes instead of
JSR opcodes as you really wanted. With the change command, you
can change all of the JMP's to JSR's from the Command Mode.
Here's how to do it:
:C "JMF'JSR"
Take special note of how we used the quotation marks ("). There
are three of them instead of four (two around each string.) When
63
you press RETURN, you will be asked whether you want Afll) or
S(ome) of the strings changed. If you press 'A' for all, every single
instance of the change will be made, while if you enter 'S' for some,
you will be given a choice at each instance to make the change or not
by entering 'Y* for "Yes you want the change" and RETURN if
you do not want to change. You can change single lines or line
ranges by putting a single line number or range (e.g. 4, 19) right after
the C. Try it out with our little program in memory.
:C "JSR"JMP"
Press RETURN, and list the program. The JSR in line 1 is now
JMP. To turn it back to JSR, from the Command Mode enter,
:C "JMP'JSR"
and press RETURN.
COPY. Sometimes you will write some code and want it repeated
elsewhere in your program. Instead of having to key it in again, you
can use the COPY function to do it automatically. Try the follow-
ing with our two-liner in memory (If you have a 3-line program in
memory, get rid of the first line with Dl {RETURN} using the
D(elete) function.)
:COPY 1 TO 2
Press RETURN and list your source code. It will now look like this:
1 JSR $E544
2 JSR $E544
3RTS
The COPY function placed Line 1 where Line 2 was and moved
Line 2 to Line 3. You can also move code upwards. Let's put Line 3
in Line 1.
:COPY 3 TO 1
64
Now your listing looks like this:
1 RTS
2 JSR $E544
3 JSR $E544
4 RTS
You can also move a range of lines by specifying the range and the
line where you want to insert the range. For example,
:COPY 10,20 TO 33
will duplicate the range of lines from 10-20 where Line 33 is. You
probably won't be using this function a lot at first, but it sure saves
a lot of time when you do need it.
MOVE. This function is just like COPY but it deletes the original
line or range after it MOVEs it. This is handy when you find you
have a line in the wrong order. To see how it works, using the
D(elete) function, get rid of those lines in our program we added
with the COPY function. (Dl {RETURN} and D2 {RETURN}).
Now enter the following and press RETURN:
MOVE 2 TO 1
When you list your program it now looks like this:
1 RTS
2 JSR $E544
Let's see if you can get it back to the original order using MOVE.
(I'm not telling you how.)
R(eplace) The R(eplace) command is something like the D(elete)
command except it puts you into the Insert Mode in the line you are
replacing after first deleting the line. For example, enter the follow-
ing and hit RETURN:
:R2
65
You will find yourself in Line 2. Hit the space bar and enter ABC as
an opcode. (There's no such opcode is used simply for illustration.)
When you list your program, it will look like this:
1 JSR $E544
2 ABC
Now using the R(eplace) function, see if you can change the ABC
back to RTS.
Finally, there are some miscellaneous other editing functions of
Merlin you should understand. If you have a program in memory
and you want to get rid of it, just enter NEW as in BASIC from the
Command Mode. To toggle upper and lower case while in the
Add/Insert mode, press the F7 function key. (You don't want
lower case characters in the Command Mode.) Finally, to convert
decimal numbers to hexadecimal numbers or hexadecimal numbers
to decimal numbers from the Command Mode just enter the
number you want converted and press RETURN. For example to
convert hex to decimal, preface your number with a dollar sign ($).
Let's say your program loads at $C000 and you want to know what
the SYS value is. Do the following:
:$C000 {RETURN}
49152 = -16384
You can SYS either number 49152 or 16384 to run your machine
language program after it's loaded into memory. To convert from
decimal to hexadecimal try the following:
:1234
$04D2
You'll find this function very useful in converting back and forth
between hexadecimal and decimal values.
66
LOADING AND RUNNING PROGRAMS
There are at least three ways to load and execute machine
language programs created with Merlin. The easiest way is to
LOAD the program from BASIC and then SYS the beginning ad-
dress of your program. You have to use a special LOAD format,
however.
LOAD "MACHINE PROG",8,1
Normally when you LOAD a BASIC program, you just enter,
LOAD "BASIC PROG",8
With machine programs, however, you have to add the ',1' after
the '8' . The * , 1 ' tells the program where to load in memory. The on-
ly problem with this method is remembering what the starting ad-
dress of your program is. One trick I use when saving object code is
to append the starting address to the end of the file name. So instead
of just saving "MAC CODE", I'll name it "MAC CODE $C000"
or "MAC CODE 49152" so that when I load it from BASIC I'll
know what value to SYS.
Another way to execute object code is from Merlin. From the Ex-
ecutive Mode just choose 'G' for "Go!" and enter the object code
file name when prompted with Run:. DO NOT put the '.o' extender
on the name. Merlin does that automatically for you. The problem
with this method is that you'll be popped back into the Executive
Mode after the program executes. Also, if the code conflicts with
the memory used by Merlin, you may crash!
Finally, you can load the source code, and then using ASM from
the editor, assemble your code. Then just exit Merlin and SYS your
program. This is a good method when you're still working on a pro-
gram, since you can de-bug it by re-entering Merlin and working on
the source code.
67
THE SOURCEROR
If you've been accumulating machine code programs on
assemblers that do not save the source code, such as the Kids'
Assembler or you have the object code but not the source code from
a file, the Sourceror is going to be very valuable. It creates source
code from object files, including labels!
To crank up the Sourceror enter,
LOAD "SOURCEROR.O",8,1
press RETURN and when the program is loaded enter,
SYS 49152
You will be prompted with the following:
Do you want an object file loaded?
(Y/N):
Take out your Merlin Master disk and put your work disk in the
drive. Now press "Y" and when prompted, enter the name of your
object file. Be sure to enter the full name, including any \o' ex-
tender. Press RETURN and your program will be loaded. You will
be told that start address and prompts what to do next. Make a note
of the starting address of your program. Then you will be given a
page of instructions of what to do. Enter the hexadecimal value of
the beginning of your program and press the T key. (That's the
lower case *L' .) For example, if your program begins at $C000 , you
would enter the following:
C000I
Press RETURN and your program will be disassembled. Find the
end of your program, usually by locating an RTS instruction, and
enter the hexadecimal address below the last instruction in your
program and press 'q' for quit. For example, let's say your RTS is
68
at address $C0D0 and the next line, $C0D1, looks like garbage,
you would key in,
C0d1q
and press RETURN.
The program will process your data and ask you for a file name.
DO NOT add the '.s' extender to your file name since Sourceror
does that automatically. You have just saved the source code for
your object code! Now you can load and run Merlin and look at the
source code in your editor.
Once you load your Sourceror created source file in Merlin, list
it. You will notice that some of the hexadecimal numbers are
prefaced by an "H". This simply means they are (H)exadecimal
numbers. Using your editor, replace the H's with a dollar sign and
you're all ready to assemble it into a clear source file. You can add
labels and comments if you want as well. The following shows what
a source file might look like and how to change it.
1
ORG
$C000
2
3
JSR
HE544
4
STA
HD021
5
RTS
In lines 3 and 4, change the HE544 and HD021 to $E544 and
$D021. When you're finished, your code should look like the
following:
1
ORG
$C000
2
3
JSR
$E544
4
STA
$D021
5
RTS
Also, you might have some garbage at the end of your file. Just
use the (D)elete function from the command mode to get rid of it.
ASM your code and save it as a new source file.
69
MERLIN'S MONITOR
To test run your assembled programs, the monitor in Merlin is
quite handy. To enter the monitor from the Command Mode,
enter,
:MON
and press RETURN. You will be given a '$' prompt to indicate
you're in the monitor. All values are expected to be given in hex-
adecimal. (The prompt will remind you of that.)
The best way to use your monitor as a test bench is to leave out
the ORG directive in any program you're testing. In this way, the
default ORG of $8000 will be used and the monitor, editor,
assembler and your code can be co-resident in memory. Once you
have ASseMbled your source code, enter the monitor, key in,
$8000g
and press RETURN. (Remember the dollar sign ($) is the prompt;
so you don't have to include it.) Your program will now execute.
Once your program is debugged you can use any ORG you want by
inserting it in your source code and re-ASseMbling it.
You can do a lot more with the Merlin monitor, but our main
purpose is to use it as a test bench. Take a look at your Merlin
manual for its other uses. To get back into your Editor, enter Y
and press RETURN. If you enter 'q' {RETURN} you'll be return-
ed to the Executive Mode.
SOME EXAMPLES
To get you rolling, let's look at a couple simple examples.The
first one just clears your screen and changes the printing characters
from blue to black. Actually, it works about the same as using
CHR$ codes to put things on your screen.
70
1
ORG
$C000
2
JSR
$E544
;Jumps to clear
routine
3
LDA
#144
4
JSR
$E716
;Jumps to screen
output
5
LDA
#65
6
JSR
$E716
7
LDA
#66
8
JSR
$E716
9
RTS
;Returns from
subroutine to BASIC
:ASM
When you are finished assembling the program, enter 'Q' to
return to the Executive Mode and then save both the source file and
object file. When you load and execute the program it will print
black letters, AB in the upper left hand corner of your screen.
This next program changes your background color to black, your
border to purple and prints white letters. There's a trick involved.
Notice that Line 2 is blank. To get blank lines with Merlin, you have
to hit the space bar once when you come to a new line and then
RETURN. (If you just hit RETURN without the space bar, you'll
go into the Command Mode.)
1
2
3 START
ORG
$C000
JSR
$E544
4
LDA
#0
;Color code for black
5
STA
$D021
;Store in background
6
LDA
#4
reg.
;Color code for
7
STA
$D020
purple
;Store in border reg.
8
LDA
#5
; ASCI I control code
for white letters
9
10 END
JSR
RTS
$E716
;Output to screen
:ASM
71
You're not expected to know how this works, but you soon will.
The START and END labels really don't do anything in this exam-
ple, but later we'll see how easy they can make life in assembly
language programming.
72
CHAPTER 4
THE COMMODORE 64
MACRO ASSEMBLER
DEVELOPMENT SYSTEM
THE PARTS
To understand the Commodore assembler package, the first
thing to understand is that it is in several parts. When you load one
part, you do not necessarily have access to another. That is, you
have to separately load the various files that make up the package.
The major elements of the package include the following:
1. EDITOR64
2. ASSEMBLERS
3. CROSSREF64
4. LOLOADER64
5. HILOADER64
6. MONITOR$8000
7. MONITOR$C000
The disk version also includes the DOS WEDGE64, a disk
operating system. The most relevant items are EDITOR64,
ASSEMBLER64 and the two loader programs. We'll discuss their
use in detail with only secondary attention to the other files. Since
editing code works a lot like editing a BASIC program, it's a good
idea to load the DOS WEDGE64 and use it just as you would when
73
programming in BASIC. The easiest way to do that is to LOAD
"BOOT ALL",8 and then RUN it.
EDITOR64
You write your assembly language programs in the EDITOR64
mode, save the source code to disk and then assemble the code with
ASSEMBLE64. Since these two operations require two different
files, we will treat their operation as two distinct events. To start,
enter the following:
LOAD "EDITOR64",8,1 {RETURN}
SYS 49152
After you do that, your screen should look like this:
COMMODORE 64 EDITOR V07282
(C) 1982 BY COMMODORE BUSINESS MACHINES
READY.
It looks like nothing has happened, but you are all set to start
writing assembly language code. You do this by entering line
numbers just as in BASIC. The four fields are:
1. Label
2. Opcode
3. Operand
4. Comment
They are arranged as follows:
LABEL OPCODE OPERAND ;COMMENT
The COMMENT field is indicated with a semi-colon before the
comment. Each time you want to go to a different field, you
simply0press the space bar. We will use line numbers beginning
with 10 and incremented by 10. This will give us room to insert code
if we need it. To get started enter the following:
AUTO 1© {RETURN}
74
Nothing will appear to happen at this point. Now, just start writing
code beginning with line 10.
10 JSR $E544
Put two spaces after the line number and one space after JSR. As
soon as your press RETURN, your screen should look like this:
10 JSR $E544
20
The new line number, 20, automatically pops up. You are in the
LABEL field; so press the space bar to get into the OPCODE field
and enter the following:
10 JSR $E544
20RTS
You have just written a complete (but small) assembly language
program. The first line jumps to a built-in subroutine to clear the
screen and the second line returns you to BASIC. However, the
Commodore assembler requires a special pseudo-opcode at the end
of all programs, .END. So press RETURN and enter the following:
10 JSR $E544
20RTS
30 .END
Now when your code is assembled with ASSEMBLER64, it will
know the end of the program. However, it also needs the beginning
of the program. Since the beginning should be at the beginning, we
will insert a line just as we can do in BASIC. So enter the following
line:
5* =$0000
15 -«Just press RETURN when you get the 15}
75
Now LIST your program and it should read:
5 *=$C000
10 JSR $E544
20RTS
30 .END
READY.
The asterisk (*) is the symbol used to define the starting address of
your program. (Many assemblers use ORG as a psudeo-opcode in-
stead of the asterisk.) The dollar sign ($) in front of the address in-
dicates it is a hexadecimal value. You could have entered,
5 * =49152
if you wanted a decimal value.
Next, since this still doesn't look very much like an assembly
language program, let's format it. Just enter,
FORMAT
and press RETURN. Your program now appears as this:
5
* = $0000
10
JSR $E544
20
RTS
30
.END
READY.
As you write your code, it is a good idea to FORMAT it every now
and then so that you can better see the separate fields. We have not
put anything in the LABEL field yet, but if we did, the spaces bet-
ween the line numbers and OPCODES would have our labels.
Similarly, if you add a comment, it too will be formatted to the cor-
rect field.
Since we inserted our starting address for the code, our line
numbers are out of whack. We can reNUMBER them with the
NUMBER command. Do the following:
76
NUMBER5,10,10
{RETURN}
After NUMBER the first
number is the old start line, the
second number is the new start
line and the last number is the
step size. So, since our old first
number was 5, and we wanted
our new first number to be 10
and we wanted our line numbers
incremented by steps of 10, we
used the 5,10,10 combination.
Just remember the format for
NUMBER as:
NUMBER(Old start),
(New start),(lncrement)
I'd like to renumber
please.
Now when you LIST your program it looks like this:
10*=$C000
20 JSR $E544
30RTS
40 .END
READY.
= =MAKING A HEADER = =
While you're learning assembly language, it might be a
good idea for you to make a header that describes the dif-
ferent fields for you. You could put in Line 1 something
like the following:
1 LABEL OPCODE OPERAND ;COMMENT
When you FORMAT your code, these fields will line
up over your code to show you if you have your labels,
opcodes, operands and comments in the correct places.
Before you save your program to disk, just delete Line 1
so it will not be assembled and mess up your object code.
77
Now your program is all set to assemble into object code. (Object
code is the machine language code that will run.) To do this you
have to first put your source code (the code you just wrote) onto
disk. Rather than using the SAVE command, you use PUT or
CPUT. So let's try it.
PUT "TESTIS"
You can optionally include parameters for starting line, ending line,
device number (the default is 8, your disk drive) and the secondary
address. If you use CPUT instead of PUT, your file will be more
compact on your disk. (Think of it as CompactPUT.)
At this point you can assemble your program. You are finished
with everything you have to do in the EDITOR64. However, let's
look at the other editing features before we go on.
For the most part, you edit assembly source code just as you
would a BASIC program. Thus, there are no special editing com-
mands for normal source code editing. However, there are some
very useful added editing functions in EDITOR64. Let's look at
them.
= =DOS WEDGE64 HELPS A LOT= =
It's a very good idea to load your DOS WEDGE64 when
you're working in the editor. With it, you can look at
your directory without destroying your program in
memory. Thus when PUTting a file to disk, you can first
look at the disk directory with > $ to see what file names
are taken up already. When you GET a file from disk,
you can check to see if the file name you want is on the
disk.
78
Look at your directory to make sure your source code is saved as
a SEQ file and then enter NEW to clear memory. Now, using the
GET statement, we'll see how to load source code from your disk.
Enter,
GETTEST1.S" {RETURN}
Your program will be loaded to disk, and you can LIST it. When
you do, you'll see the following:
1000 * = $C000
1010 JSR $E544
1020 RTS
1030 .END
READY.
As you can see, your lines now start at 1000 instead of 10. Your
editor automatically does this for you. (Don't ask me why.)
Anyway, when you're working on source code, you can save a part
of it and then re-load it with GET and continue your work.
ADDED EDITING FUNCTIONS ON EDITOR64
CHANGE. Sometimes you will want to make a lot of changes in
your program. To do this quickly with a single string, the
CHANGE command is very helpful. Let's say you accidentally us-
ed LAD instead of LDA. The CHANGE command would go
through your program and change all instances of LAD to LDA.
Using your program in memory, we'll change JSR to JMP. Do the
following:
CHANGE/JSR/JMP/ {RETURN}
1010 JMP $E544
The changed line pops up showing you that your JSR is now JMP.
With just one change, it's probably just as easy to edit as you would
in BASIC. However, when you have several opcodes, labels or
other strings to change, you will really appreciate this function.
79
DELETE. This command would be handy with BASIC pro-
grams. If you want to get rid of a range of lines, you just enter, for
example,
DELETE 1010-1030 {RETURN}
and lines 1010-1030 will be deleted. This is very helpful when you
find a way to tighten up your code and you want to get rid of extra
lines. All you have to do is enter the first line to the last line of code
you want DELETEd.
FIND. When your programs get really long and you want to find
a string, the FIND command is a big help. (If you DELETEd your
lines in the above example, you better GET "TEST1.S" back into
memory.) Enter, for instance,
FIND "RTS" {RETURN}
1020 RTS
Notice that the FIND command requires quotation marks around
the string you are trying to find, while the CHANGE command
does not want the quotation marks.
Finally, if you want your EDITOR64 out of action, just enter
KILL {RETURN}. You can get it back with SYS 49152. That's
about it for editing. Remember, you edit most of your code just as
you would a BASIC program. The added editing commands simply
make it a lot easier for you.
ASSEMBLERS
Now that you have your source code saved as TEST1.S, you are
ready to assemble it with ASSEMBLE64. To get started do the
following:
LOAD «<ASSEMBLER64",8 {RETURN}
RUN {RETURN}
80
Unlike the EDITOR64, you do hot have to SYS an address to get it
up. You just RUN it. As soon as you RUN the program you will
see,
CBM RESIDENT ASSEMBLER V080282
(C) 1982 BY COMMODORE BUSINESS MACHINES
OBJECT FILE (CR OR D:NAME)
Now BE CAREFUL here! You are asked to enter the name for the
OBJECT file. You saved the SOURCE file to disk under the name
TEST1.S, and you DO NOT want the same name for your OB-
JECT file. Enter the name TESTl.O and hit RETURN. By using
the extenders *.S' for source files and '.O' for object files, you won't
get into as much trouble.
Next you will be asked,
HARD COPY (CR/Y OR N)?
If you have your printer hooked up, enter Y or just press
RETURN. If you do not have a printer hooked up and on-line enter
N and press RETURN. Next you will be asked,
CROSS REFERENCE (CR/NO OR Y)?
You won't need a cross reference until you get into more com-
plicated programs; so enter N or just RETURN.
Now you will be asked the SOURCE file name.
SOURCE FILE NAME?
You saved your source file as TEST1 .S so enter that name and press
RETURN. Your screen will show the following:
PASS1
TESTIS PAGE 0001
LINE# LOC CODE LINE
PASS2
81
00001 0000
* = $C000
00002 COOO
4
00003 CO03
20 44E5
JSR $E54
60
RTS
00004
.END
ERRORS = 00000
END OF ASSEMBLY
READY.
If that's what you got, you correctly entered and saved your
source code, assembled your object code and saved the object code
to disk in a SEQ file. Look at your directory to make sure. You
should see both "TEST1.S" and "TESTl.O". If you did not get
that, then enter SYS 49152 to get back into the editor, GET your
source code, make corrections and try it again.
LOADERS
One thing I'm not crazy
about with this assembler is the
way object code is loaded and
executed. Most object code can
be loaded with LOAD "FILE-
NAME"^,! and then SYS the
load address. However, since
the files are stored as SEQ files
instead of PRG files, that's not
possible. (It is nice for cassette
users though.) You have to load
either LOLOADER64 or HI-
LOADER64. If you load your
program in higher memory loca-
tions, such as we did at $C000
(49152), use the LOLOADER64
since it will place itself in a posi-
tion that will not conflict with
the code up in $C000. (It loads
at $800). If your code is in a
HILOADER
LO LOADER
82
lower address, such as the cassette buffer at 828, then use the
HILOADER64. To crank them up you have to use two different
formats:
LOAD "LOLOADER64",8,1 {RETURN}
RUN {RETURN}
LOAD "HIGHLOADER64",8,1
SYS 51200
Let's do it and see if our program works. Load the LOADER64
and RUN it. Your screen will look like this:
LOLOAD.C64 V072772
(C) 1982 BY COMMODORE BUSINESS MACHINES
HEX OFFSET (CR IF NONE)? {RETURN}
OBJECT FILE NAME? TEST1.0 {RETURN}
C000
C003
END OF LOAD
READY.
The HEX OFFSET refers to the number you want to add to your
load address. If you wanted to load your program at $C200 and it
was already at $C000, your HEX OFFSET would be $200. Just
press RETURN since we want it to load at $C000 (49152). Enter
the filename, TEST1 .0, press RETURN and you will see the begin-
ning address of your program and the end of it (C000-C003). Now
we're all set to give it a go. Hold your breath and enter,
SYS 49152
If your screen clears and all you can now see is the READY, pro-
mpt, your program works. If it locks up your screen, blows out the
windows and sets your hair on fire, try again.
THE MONITORS
Like your loaders, the Commodore assembly language package
comes with two monitors. MONITOR$8000 loads in the low por-
83
tion of RAM and MONITOR$C000 in the high portion. Load the
monitor in the low portion of memory and we will use the high
RAM for some examples.
First of all, you may be wondering what a monitor is. Basically, a
monitor is a device that allows you to examine and change your
code in memory, usually in hexadecimal values. Monitors also
enable you to disassemble machine language programs in memory
and see the status of the various registers in your microprocessor.
(We'll discuss registers in Chapter 6.)
Your Commodore monitors can also be used as assemblers,
AND they can save machine code as PRG files. This means that you
can load these files directly from BASIC without having to use the
loader programs. (Why didn't they do this with ASSEMBLERS?!)
Therefore, if you want to assemble your code so that it can be
stored as a PRG file, you can use the mini-assembler in your
monitor to write assembly programs.
First, LOAD "MONITOR$8000",8,1 and SYS 32768. When
you first get into your monitor, you will see the following:
8*
PC SR AC XR YR SP
•803E 32 00 83 84 F6
The period (.) is your monitor prompt, awaiting your command.
The PC, SR, etc. at the top refer to:
1. PC = Program counter
2. SR = Status register
3. AC = Accumulator
4. XR = X register
5. YR = Y register
6. SP = Stack pointer
The values of the various registers is the number immediately below
the initials. For example, the accumulator has '00' in it. When you
84
get to Chapter 6, you can use your monitor to examine these
registers.
For now, let's see how to assemble code in the monitor. The com-
mand to enter assembly code is 'A' . Do the following to get started:
.A C000 JSR $E544 {RETURN}
As soon as you press RETURN your code is assembled and your
line now looks as follows:
.A C000 20 44 55 JSR $E544
.AC003
The numbers to the left of your mnemonic opcode and operand are
the machine language opcode and operand. The next available ad-
dress is C003, and so you're all set to enter more assembly code.
FIRST hit the space bar and then enter the next line. After each
RETURN, you have to hit the space bar first before entering your
opcode.
.A C000 20 44 55 JSR $E544
.A C003 LDA #$00 {RETURN}
.A C005 STA $D021 {RETURN}
.A C008 RTS {RETURN}
Each time you hit return, the line you had just entered will insert the
machine language code. When you're finished with the above pro-
gram will look like this:
.A C000 20 44 55 JSR $E544
.A C003 A9 00 LDA #$00
.A C005 8D 21 D0 STA $D021
.A C008 60 RTS
.AC009
Compared to using EDITOR64, this is a little messy, and it
assembles your code each time you hit return. You can edit your
code, but inserting code between addresses is complicated. For the
85
time being, let's just see how to save a PRG file using the above pro-
gram.
To save a machine code program as a PRG file, simply press
RETURN after you are finished to get the period (.) prompt
without the 'A' and address. The 'S' command is used to S(ave)
PRG programs to disk or tape. Use the following to save the above
example:
.S"MONPRG.O",08,C000,C009 {DISK}
.S"MONPRG.O",01,C000,C009 {TAPE}
The parameters after the file name include:
1. Device number; 01 for tape and 08 for disk.
2. Starting address.
3. Ending address plus 1.
Notice in our little program that we started at SC000 and ended at
$C008. Since $C008 + 1 = $C009, $C009 was our value used to
Save the program. Until you become comfortable with adding in
hexadecimal, just use the last address on the screen before you left
the 'A' mode.
To test your program from the monitor, you use the 'G' for
"Go" command. Since our program began at $C000, we would
enter,
.G C000 {RETURN}
You should have seen your screen clear and turn black. The little
program simply loaded the value for black (0) into the address for
the background color ($D021). To get back to the monitor, just key
in SYS 32768.
To see if your program really was saved as a PRG file, turn off
your computer and disk drive and re-start it. Load your program as
follows:
LOAD "MONPRG.O",8,1
86
When you get your READY, prompt, key in:
SYS 49152 {RETURN}
If your program is correctly saved, it should immediately clear your
screen and turn it black. That's a lot easier than using the
LOLOADER or HILOADER programs to get your machine pro-
gram in memory.
= = CONVERTING YOUR EDITOR64 FILES = =
If you want to convert your SEQ object code files
created with EDITOR64 and ASSEMBLER64 into
PRG files, you can do so (painstakingly) with the
monitor. First, load your SEQ object program with
LOLOADER or HILOADER depending on where your
files loads. Then, load the corresponding monitor pro-
gram. (MONITOR$8000 if LOLOADER and MONI-
TOR$O000 if HILOADER.) Disassemble your object
code using the D command and starting and ending ad-
dress of your object file. For example, you would enter
the following for our program:
.D C000.C008 {RETURN}
If you're not sure of the ending address, just disassemble
your code a few addresses at a time until you find the end.
Then using the S(ave) command from your monitor, save
your program using '.OP' (for OBJECT PRG) as an ex-
tender.
Other Monitor Commands
Beside the A, G and S commands, your monitors have several
others as well. Here's a summary of them.
L for L(oad). This re-loads PRG files created with monitor
Save. Use the following format:
87
.L"FILENAME",DEVICE NUMBER
The device number is usually 08 for disk and 01 for tape.
.D for Disassemble). This disassembles code from a specified
starting address to a specified ending address. For example, we
could look at our illustration program with the following:
.DC000C008 {RETURN}
.M for M(emory dump). This is something like disassemble,
but you are given the hexadecimal machine code in memory
locations specified. For example, dump our example program
to memory with the following:
.M C000 C008 {RETURN}
Compare it with what happened when you used the D com-
mand.
.R for R(egister Display). When you learn about registers in
Chapter 6, you may want to see the status of the various
registers. You just enter R and press RETURN.
There are some other monitor commands we won't get into since
they are used for larger programs than we will deal with in this
book. Practice with the ones we've discussed to learn how machine
code is stored in memory.
SOME EXAMPLES
O.K., crank up your EDITOR64 and let's see some examples.
We'll use the comment and label fields. To get going enter AUTO
10 {RETURN}.
10
;LABEL OP OR COI\
20
*=$C000 ;LOAD ADRS
30
JSR $E544
40
LDA #0
50
STA $D021
60
LDA #5
70
STA $E716
80
RTS
90
.END
88
Our entire first line is a COMMENT. The semi-colon lets the
assembler know that it is only source code that is not to be compil-
ed. When you FORMAT your code, you can see if your assembly
code lines up under the correct label. If it does not, edit the lines
after LISTing them. Also while editing, turn off AUTO by just
entering AUTO {RETURN}. The FORMATted listing should ap-
pear as the following:
10
;LABEL OP OR
COMMENT
20
* = $C000
;LOAD ADRS
30
JSR $E544
40
LDA#0
50
STA $D021
60
LDA#5
70
STA $E716
80
RTS
90
.END
We did not use the LABEL field, but you can see the spaces
where labels would be when FORMATted. By frequently FOR-
MATting your code, you can check for the correct fields. Now
PUT this on disk and assemble it, and then load and run it. It'll turn
your screen black and your letters white. The next one turns your
border purple and your background yellow. (It looks like your
sister's first attempt applying make-up.)
10 ;LABEL OP OR COMMENT
20 * = $C000 ; LOAD ADRS
30 JSR $E544
40 LDA #4 ; PURPLE
50 STA $D020 ; BORDER ADRS
60 LDA #7 ;YELLOW
70 STA $D021 ;BACKGROUND ADRS
80 RTS
90 .END
With more comments, you have a better idea of what's going on
in the program. Later when we need labels, we'll see how they make
loops very easy. Now, go on to the next chapter and take a look in-
side your computer.
89
90
CHAPTER 5
STRANGE NEW NUMBERS
DECIMAL, BINARY AND HEXADECIMAL NUMBERS
If you've been following our examples, you probably noticed
some funny numbers with dollar signs in front of them, like $E544
and $D021. Now with a perfectly good numbering system we all
understand, why confuse matters with funny numbers called hex-
adecimals? The reason lies in some even weirder numbers called
"binary" and the workings of your microprocessor.
To begin with, suppose instead of counting on our 10 fingers, we
used just our two arms. After all, the 10 digits in our numbering
system is based on our 10 fingers. Since we're used to our 10
fingers, our numbering system seems natural. However, our two
arms are natural, and we could have just had to remember two
digits instead of ten if we had counted with our arms instead of our
fingers. Let's count from to 10 with our decimal numbering
system and think about what's going on:
1
2
3
91
4
5
6
7
8
9
10
Everything is lined up fine until we get to 10 , and then we have to
start all over again with our 10 digits (0-9) adding a zero onto our
second digit. To keep things neat, let's add a zero before our first
digit:
00
01
02
03
04
05
06
07
08
09
10
That's much better since our numbers are all lined up. Now all we
did in this system is clear. With our 10 digits (0-9), we use all of
them in our far right column, and when we run out, we simply tack
on a 0(start over) and increment our second from right digit by 1.
How would the same thing look with only two digits, and 1?
00
01
10
11
We ran out of digits pretty quick! Compare that with our decimal
system:
92
©0
00
01
01
10
02
11
03
In other words, using our arms instead of our fingers, we can only
count to 3 before we run out of digits and have to add a third digit.
Okay, so we can count by a two digit system instead of the ten
digit system. But why? The reason lies in the way your computer
really works. Basically it reads electrical currents as either being on
or off. If a current is read as "ON" then it is read as "1." If it is
read as "OFF" it is read as "0." Now if you think computers are
pretty dumb, you're right. However, while they may be dumb,
they're very fast. As a result, your Commodore 64 can interpret a
bunch of 0's and l's into decimal numbers and strings and look like
it's really smart.
Your 6510 microprocessor is an 8 bit device. That means that it
has 8 cells that read a current in each cell (bit) as being ON or OFF.
Just about everything else in the computer is arranged to deal with
those 8 bits in the microprocessor. Knowing this, we know that we
can have a two digit numbering system 8 digits long.
7 6 5 4 3 2 10
10 10 10 1
- + - + - + - +
The plus and minus signs underneath the values indicate whether
the current is ON or OFF. The 8 values (7-0) above the digits repre-
sent the way byte is arranged. Thus when we say that Bit 6 is ON, we
know we're talking about the second bit from the left.
Our two digit binary system can count from 00000000 to
11111111. In decimal, that's to 255 . However, since to 255 is an
uneven system, computer programmers used a 16 digit system
93
numbered from $0 to $FF. To see why, let's break down our 8 bit
number into two parts. Each part is called a "nibble".
7654
0101
3210
0101
Now, let's see how much we can pack into a two digit system with
four places and put our decimal and hexadecimal numbers next to
them. Binary numbers are often indicated with a percent sign; so
let's include one here.
Binary
Decimal
Hexadecimal
%0000
0000
$0000
%0001
0001
$0001
%0010
0002
$0002
%0011
0003
$0003
%0100
0004
$0004
%0101
0005
$0005
%0110
0006
$0006
%0111
0007
$0007
%1000
0008
$0008
%1001
0009
$0009
%1010
0010^-
$000A
%1011
0011
$000 B
%1100
0012
$000C
%1101
0013
$000 D
%1110
0014
$000 E
%1111
0015
$000 F
Now let's look again, but this time, we'll clear all the leading
zeros from the hexadecimal values and leave out the decimal.
Binary
Hexadecimal
%0000
$0
%0001
$1
%0010
$2
%0011
$3
%0100
$4
%0101
$5
94
%0110
$6
%0111
$7
A^^^BI
%1000
$8
%1001
$9
%1010
$A
$&§&*$
%1011
$B <
%1100
$C
■OCTjOW ^^^^
%1101
$D
^Hib» "*^
%1110
$E
%1111
$F
Sweet $10
As you can better see, the hexadecimal numbering system can
represent an entire nibble with a single digit. The decimal system has
to start using 2 digits when the binary system counts to 10 10 .
1111 -^-Binary
F -^-Hexadecimal
Now since an 8 bit grouping, which is called a "byte", is compos-
ed of two nibbles, we'll have to see what maximum number in hex-
adecimal can be stored in a byte.
1111 1111 =11111111
$F $F =$FF
255 x 256 + 255 = 65535
Since there are over 64000 bytes in your Commodore 64, a
number system that keeps track of what is in each byte with only
two digits is more efficient than one that takes up to five digits, as
would the case be with a decimal system. (By the way in case you
didn't know, the "64" in Commodore 64 stands for 64 kilobytes. A
kilobyte is 1024 bytes. Thus your Commodore has 64 x 1024 bytes
of RAM.)
At this point, no one expects you to understand everything about
these weird numbering systems. Rather, all you should be able to
grasp is the concept that its easier to use a maximum of two digits to
represent what is in a byte than five. There are all sorts of conver-
sion programs that will allow you to convert numbers into good old
decimal numbers. Your assembler will do it automatically for you.
95
Later on when you've worked with hexadecimal and decimal
numbers for a while, it will be a lot simpler. In the meantime, you
can use decimal numbers in assembly programs if you want.
To re-cap, here's what's going on in your computer and why hex-
adecimal numbers are easier to use:
1. The processor interprets electrical currents as being ON or
OFF
2. The ON/OFF configuration is stored in an 8 bit binary
number or byte.
3. The 8 digit binary number is represented as a two digit hex-
adecimal number.
4. When you look at a disassembly of machine programs, you
see hexadecimal numbers representing values in bytes.
GOING BETWEEN NUMBER SYSTEMS
Using conversion charts
Probably the best way to convert numbers when you're working
with your computer is with number conversion charts. In this way,
you can keep your computer free for programming and still make
the conversion. In the Appendices, there are several charts which
give addresses in both decimal and hexadecimal. In this way, you
can quickly find an address, the token of a machine language op-
code or other information in both decimal and hexadecimal.
However, since the computer works with bytes, and each byte can
only hold a value of $FF or 255 (actually 256 since you can enter
values from 0-255), you have to make conversions on your own
when the number is greater than 255. This will help you understand
something about how the computer "thinks," and how you have to
think in writing assembly language programming.
Now since all the computer can stuff in a single byte is 255, how
does your computer deal with numbers larger than 255? It has to
use two bytes. One is called the high byte and the other is called the
low byte. Let's look to see how 256 looks in a two byte, high byte-
low byte configuration:
96
High Byte Low Byte
76543210 7654321©
00000001 00000000 ^-Binary
$01 $00 -*-Hex
1 00 ^-Decimal
By multiplying the decimal number in the high byte by 256, we can
get the high byte value of our decimal number. Since the Low byte is
zero in our example, we just add the high byte and low byte to get
256. The hexadecimal value doesn't have to be changed at all. We
just tack on the two zeros in the low byte to the $1 in the high byte to
get $100, the hexadecimal value of decimal 256. Now suppose in
PEEKing at a couple of addresses we found a high byte-low byte
value of the following:
High Byte Low Byte
12 33
To determine what that is in hex is actually easier than determin-
ing the decimal value of the combined number. Looking at a
decimal-hex conversion chart, we find that the 12 is $0C and the 33
is $21 . The hex value is $0C21 . The decimal value is 256 x high byte
+ low byte. Thus, 12 x 256 + 33 = 3105, and now we know that
$0C21 = 3105. We did that with only a conversion chart from
0-255 ($0-$FF).
Now, how about going the other way? Let's say we have the hex
number $ABCD. First, we divide it into high byte and low byte get-
ting:
High Byte Low Byte
$AB $CD
Our conversion chart shows us that $AB = 171 and $CD = 205.
Now we have a decimal high byte of 171 and low byte of 205. We
multiply 171 x 256 and then add 205 to get 43981. Your assembler
takes care of these matters for you, but as you get further and fur-
ther into machine and assembly language programming, you'll be
looking up more and more numbers.
97
CONVERSION PROGRAMS
Sometimes you will be planning out a program and want to look
up values quickly. You won't be entering code on your computer,
but instead planning a program on paper making a lot of conver-
sions between hex and decimal. While the charts are handy, you
want something a little simpler. (This is especially true if you want
to POKE or PEEK values in BASIC.) The following program will
do that for you. Pay special attention to the algorithms beginning in
Lines 240 and 300.
HEX/DEC DEC/HEX CONVERSION
10 PRINT CHR$(147)
20 INPUT "DEC OR HEX CONVERSION (D/H)Q = QUIT
";C$
30IFC$ = "H"THEN200
40IFC$ = "Q"THENEND
50IFC$<> "D" THEN 2©
60HEX$ = "":N=0
70 INPUT "DECIMAL VALUE";N
8©HB=INT(N/256)
90LB=N-INT(N/256)*256
10©FORX=1TO2
110IFX=1 THEN N = HB
120IFX = 2THENN = LB
130 N% = INT(N/16) :GOSUB 310
140 N% = N-N%*16:GOSUB 310
150 IF X = 1 THEN H1$ = HEX$ : HEX$ = ""
160IFH1$ = "0"THENH1$ = "00"
170 NEXT
180HEX$=H1$ + HEX$
190 HEX$ = "$" + HEX$ PRINT "HEX= "; HEX$:
PRINT: GOTO 20
200H$ = "":DE =
210 INPUP'HEX VALUE ";H$
220 GOSUB 250
230 PRINT "DECIMAL VALUE = ";DE
240 PRINT : GOTO 20
2^ff) RFM **********************
98
260 REM CONVERT HEX TO DECIMAL
07C\ RFM **********************
280 FOR L=1 TO LEN(H$) : HD = ASC(MID$(H$,L,1))
290 DE = DE* 16 + HD-48 + ((HD > 57)*7)
300 NEXT L : RETURN
*3i(n rfm **********************
320 REM CONVERT DECIMAL TO HEX
Wl\ RFM **********************
340 HEX$ = HEX$ + CHR$(48 + N % + 7 * ABS(N % > 9))
350 RETURN
When you RUN the program, just choose 'H' to convert from hex-
decimal and 'D' to convert from Decimal-hex.
Binary-Hex-Decimal
In converting between hex and decimal, we broke down the hex
value into the high byte and low byte values. With binary conver-
sions, we'll break 8 digit binary values into 4 bit nibbles. Each nib-
ble converts into a single digit hex value. Then, by putting the hex
values in each nibble together, we have a one byte hex value.
Byte
High Nibble
Low Nibble
7654
0101
3210
1110
Going back in this chapter to our binary-hexadecimal comparison,
we see that the high nibble's binary value of 0101 (or %0101 since
High Nibble
Low Nibble
99
binary values are indicated with a percent sign) is $5 and the low
nibble value of %1 1 10 is $E. Thus, the final hex value is $5E. You
can translate this into decimal by using the hex-decimal conversion.
If you don't already know how to convert directly from binary to
decimal, here's a simple way to do so. Take the byte as a unit and
use the multiples above the specified bit. After finding the multiple
value of each bit, add them all up to get your decimal value.
128 64 32 16 8 4 2 1 ^- Multiple
7 6 5 4 3 2 1 *«- Bit
1 10 10 1 •*- Binary
number
+ 64 + + 4 + + 4 + + 1 =73 Decimal
If you understand powers the bits can be understood as follows:
2 A 7 2 A 6 2 A 5 2 A 4 2 A 3 2 A 2 2 A 1 2 A ^- Power of
two
7 6 5 4 3 2 1 ^-Bit
Notice how the power of two corresponds with the bit number.
That way you can remember the multiple simply by knowing the bit
number. Thus instead of remembering that bit 5 is 32, you can
remember it as 2 A 5 or "two to the fifth power." (On your Com-
modore 64 the A is represented by the up-pointing arrow.) Knowing
that, writing a little conversion program in BASIC should be a
snap:
10PRINTCHR$(147)
20 INPUT "BINARY VALUE "; B$
30 IF LEN(B$) <> 8 THEN 20
40 FOR X = TO 7
50 V$ = MID$(B$,X + 1, 1)
60 V = VAL(V$) : IF V < 1 THEN X=7: PRINT
"BONG!":TD = 0:BV = 0:NEXT:GOTO 20
100
70 P = 7 - X
80IFV = 1 TH EN BV = 2tP: REM UPWARD ARROW
KEY
90 TD = TD .+ BV
100BV =
110 NEXT X
120 PRINT "DECIMAL VALUE = "; TD
130 INPUT "ANOTHER(Y/N) ";AN$
140 IF AN$ = "Y" THEN TD = : GOTO 20
(If you work with sprites, the above program will help you find their
values quickly.)
HOW NOT TO WORRY ABOUT NUMBERS
Your assembler will accept just about any kind of numbers; so, if
you know the correct number, you can enter it in either hex or
decimal. As we go along we'll be discussing more and more hex-
adecimal elements of programming in assembly language because it
is easier than decimal. (If you don't think it's easier, just look at a
batch of numbers with binary patterns. With fewer digits, the
numbers get longer quicker!) At this point, though, do not be over-
ly concerned. Why worry if you can enter your programs in either
format. (Merlin and the Commodore assembler even accept binary
numbers.)
This chapter has just been a quick introduction to the number
systems used in computers. As you need it, you can always come
back and look up what you want. Don't try and attempt to be adept
at converting numbers all at once. Using your charts and programs,
you will gradually be able to see how to convert decimal into hex-
adecimal and vice versa. In the meantime, just forge ahead and en-
joy your computer.
101
102
CHAPTER 6
WHAT'S IN YOUR
MICROPROCESSOR?
The 65 10 contains seven registers, three of which we will be using
a great deal and four others which you should begin to understand.
We will be manipulating the A, X, and Y registers in our programs
a lot, while the other registers will affect what we're doing to a more
or less apparent degree. But to get started, let's begin with the no-
tion of a register.
WHAT'S A REGISTER?
Essentially, a register is something that keeps track of something
else. For example, stores keep track of the money they receive with
a cash register. It records how much money went in and how much
went out as change or refunds. Your major registers in your
microcomputer are much simpler in that most of them can only
count up to 255 ($FF) before they run out of room. In the last
chapter, we saw that your computer operates in 8 bit chunks called
"bytes." Most of the registers in your microcomputer also work in
8 bit bytes.
103
Since the registers can handle so little, you have to tell them
everything you want them to do. Think of them as some not-too-
bright friend who must be told everything. For example, let's say
you ask your little brother, "Go get my baseball bat." That's all
you need to say since you assume that he knows where the bat is. In
BASIC you can do pretty much the same thing with statements such
as GOTO, GOSUB and IF/THEN. BASIC and other high level
languages are really smart compared to assembly language. With
assembly language, you have to tell it everything you want. If your
little brother was given instructions in assembly language, "Go get
the bat," would become something like the following:
1. "Go into the house and up the stairs."
2. "At the top of the stairs, turn left and go into my room."
3. "In the room, go to the right wall where you will find a
rack."
4. "On the rack is a baseball bat"
5. "Put the bat in your hand and take it out of the rack."
6. "Turn around and leave the room."
7. "Come down the stairs and out of the house."
8. "Come to me and hand me the bat."
The reason you had to give this large set of instructions is because
your little brother can only handle one byte at a time. (Once you
give him the instructions, though, the little kid sure is fast!)
The instructions in assembly language are similar to giving in-
structions to someone who can only handle one small parcel at a
time. The size and complexity of the instruction is dependent on the
size of the register. Since most of the registers are eight bits, the in-
structions must be relatively small.
THE ACCUMULATOR
The most important register is the accumulator or A register. Its
contents are read by other registers, and it is used as a cross-roads in
your machine. It performs addition and subtraction, logical opera-
tions and is usually the register to send information to different
memory locations. Several mnemonic opcodes refer to the Ac-
104
cumulator. For example, the instruction LDA LoaDs the Ac-
cumulator (or A register) with some value, and STA STores what's
in the Accumulator at some address. Many instructions check to see
what's in the accumulator before taking action. Since it is an 8 bit
register, the greatest number possible in the accumulator is 255
($FF).
THE X AND Y REGISTERS
These two registers are also 8 bit ones. They can do most of the
things the accumulator can do, but they're usually used as counters
or "indexers." For example, the X or Y registers are often in-
cremented something like FOR/NEXT loops. The contents of the
register is used as an index or offset to an address. If the register
value is "1" then "1" is added to a specified address. Thus, if you
wanted to do something with a range of memory without having to
keep entering the next memory location, incrementing the X or Y
registers is a handy way of doing this. They can also be used for
temporary storage, half-way houses and transfer points. In your
mnemonic opcodes the letters X or Y are tip-offs that the instruc-
tion does something with one of these registers. For example, INX
increments the X register and TYA transfers the contents of the Y
register to the accumulator. Both registers are 8 bit ones and can
only hold a maximum value of 255 ($FF).
THE PROCESSOR STATUS REGISTER
(STATUS REGISTER)
This register is really different from the A, X and Y registers. In
fact, it is actually 7 little 1-bit registers packed into one 8 bit byte.
(One bit is not used.) Called the T' or Status register, it is affected
by what various opcodes do within a program. When we discuss
branching and looping, we will see more clearly how it works. For
now, let's take a look at its make-up.
7 6 5 4 3 2 10 ^-Bit
N V * B D I Z C ^-Register
105
Before we see what the initials really stand for, here's a way to
remember them in order:
No Very Bright Dime Is Zero Cents
Just remember "bright dimes"
are worth ten cents, not zero
cents. And what's half a dime?
It's 5 cents. That'll help you
remember bit #5 is not used.
Before we look at the 1 bit
registers, you should realize they
all have only one of two condi-
tions. You'll remember that a
single bit can either be "0" or
"1." If the register is "1", we
say that it's Flag is Up or set. If
the content of the bit is "0" then
the Flag is Down or cleared.
That's easy to remember since
each individual 1-bit register can
only be one or the other - set or
cleared. (Remember when you
clear out your drawer, it has
"0" contents.)
Negative Flag
When the results of an operation is "negative" the flag is set.
(That means there's a "1" in bit 7.) On positive results, the flag is
cleared, meaning a "0" is in bit 7. Loading a register (A,X or Y)
with a value of less than 128 will clear the N flag; loading a value of
greater than 127 will set it. Thus, "negative" refers to values greater
than 127 and "positive" to register values less than 128. (It has to do
with "two's complement" math, which we will not cover in this
book. In other words, don't worry about it.)
106
oVerflow Flag
This is set in signed arithmetic operations and cleared with a
special opcode. Don't worry about it for the time being, for at this
stage it won't concern us.
Break Flag
If the last instruction was BRK this flag is set. This is another flag
we will not be using much at first.
Decimal Flag
This flag is set and cleared for certain decimal operations with
SED and CLD respectively. We will not be concerned with this flag
as it is primarily used for determining the type of arithmetic the
microprocessor is to use.
Interrupt Flag
This flag is set if there is a hardware interrupt. We won't be going
into using this flag since it deals with hardware conditions in your
machine.
Zero Flag
If the last result was a "0"
then this flag is set. This flag will
be used a lot by our branching
instructions. The crazy thing
about this flag is that if the last
result in the accumulator was
"0" then the flag is set which
means there's a "1" in bit-1. So
that this won't confuse you, just
remember that when the last
result was zero, the Z register
gets all excited and waves a flag.
Setting a flag
107
Carry Flag
This flag is set when a carry/no-borrow condition arises. Other
logical and math operations also will set this flag. You can use it for
other things as well since there are opcodes for setting (SEC) and
clearing (CLC) the C flag. By using other instructions that read the
Carry flag, you can use it in various ways.
At the beginning level of assembly language programming, many
of the Status register conditions will not directly affect us. The big-
gest problem encountered by beginners with the Status register is
when some condition exists they accidentally created. Unexpected
results may arise they do not understand. To avoid these unknown
sources of problems, we will be very careful in not tackling some
more advanced techniques affecting the Status register. However,
as you start experimenting on your own (as you certainly should
do!), you're going to have to go beyond the scope of this book for
understanding all the possible circumstances leading to unwanted
results. However, you should be aware that the Status register is one
place to examine for the cause of unexpected results.
STACK POINTER
This register handles some important information that won't
concern you directly at first but is critical to your program. The
stack pointer holds the return address for subroutine jumps. In
BASIC when your program executes a GOSUB, it needs something
that will tell it where it jumped from. This is called the return ad-
dress. With assembly language, you will be using JSR (Jump to
SubRoutine) right away. The stack pointer tells the JSR where to
return after it has made the jump. Thus, while all the programmer
has to enter is JSR and everything will be handled automatically by
the stack pointer, the stack pointer itself is crucial to program's
operation.
The stack itself is located in "Page 1" of your memory; locations
$0100 - $01FF. (Page 2 is from $0200-$02FF, Page 3 from
$0300-$03FF, etc.) The stack works like the spring loaded tray
dispenser they have in cafeterias. As you remove each tray, a spring
108
underneath the trays pushes the
next one up. If you put a tray on
top of the stack, it will be the
first one you take off. That
means the Last In is the First
Off. This is called a LIFO ar-
rangement.
IN
87
53
53
74
74
39
39
♦
IH
87
Last In First Out
Your stack starts putting values at address $01FF, the top of the
stack. If you add another value, the stack pointer moves down to
point to the next lowest address, $01FE. If you take one element off
the stack, the stack pointer is moved up to point to $0 IFF. Since the
stack stores two-byte addresses, such as $C004, usually we're deal-
ing with two locations on the stack at a time. Thus, $0 IFF might be
$C0 and $0 1FE, $04. By moving the stack pointer down to point to
the most recently added value, the last value entered becomes the
first released. (The "last hired" is "first fired.")
As we said, you probably won't be doing much with the stack
right away since the most important work of the stack is keeping
return addresses. This is done automatically with JSR and other in-
structions that require return addresses. However, assembly'
language programmers often use the stack for temporary storage.
The stack pointer, which tells the next available location on the
stack, starts at $FF and works its way toward $00. In our above ex-
ample, where the return address of $C004 is using stack locations
$01FE and $01FF, the stack pointer points to $01FD as the next
location on the stack.
Stack
$01 FF $C0
$01 FE $04
$01 FD
Stack Pointer points here
109
Stack Pointer
$FF
$FE
$FD <*- Low byte
$01 <■ High byte is always $01
Because the stack pointer is an 8 bit register, the most it can hold
is 255 or $FF. Since it needs a way to deal with values up to $0 IFF,
it needs a high byte of $0 1 . To handle the high byte, $0 1 is always
the high byte of the stack pointer. Thus, even with an empty stack
with the pointer pointing to $01FF, the $FF is always combined
with the high byte of $0 1 to arrive at $0 1 FF. In this way, the pointer
can handle the entire stack with an 8 bit register.
PROGRAM COUNTER
The program register is a 16-bit register storing the next address
to be executed in a program. As we will see in more detail in the next
chapter in discussing your Commodore 64's memory, the program
counter stores addresses in a Low-Byte, High-Byte configuration.
The 16 bit register is actually two 8-bit registers arranged as follows:
Program Counter Low Program Counter High
76543210 76543210
LO-BYTE HIGH-BYTE
As each instruction is executed, the program counter is in-
cremented to the next address where an instruction will be stored. In
this way, your computer can execute instructions in the correct
order. However, since this is done automatically, you don't have to
worry about it.
INPUT/OUTPUT PORT
This port is located at locations $00 and $01 in your computer
and is the principle difference between the 6510 and the 6502
microprocessors. It is used for directing input and output (I/O),
110
and other than suggesting that you stay out of those locations, we're
not going to be dealing with this port at all. The $0 location is the
data direction register, and $1 is the I/O port itself.
SUMMARY
It's easy to get tangled up in the registers if you try to do too
much too soon. For the most part, your major operations will be
with the accumulator, and the X and Y registers. Your program will
be affected by the other registers, but as in BASIC where most of
the computer's operation are taken care of for you, in assembly
language programming, most of the registers take care of
themselves. Thus, while its useful to know about the P register's
flags, and we will be using instructions that rely on those flags, you
don't have to actually "hand-set" the flags. Rather, you simply will
be writing opcodes that take care of the flags for you. As you
become more advanced, you will be making more direct use of the
registers in your programs. To begin, though, you don't have to
keep track of everything the registers are doing.
So if you feel a bit lost right now, and you do not think you
understand everything about the various registers that work with
your microprocessor, don't worry. While assembly language pro-
gramming requires that you give your computer more instructions,
the bulk of what goes on is fairly automatic. Just remember to take
things a step at a time when we get into writing actual assembly
language programs. With practice and experimentation, what we
have covered will become clearer.
HI
112
CHAPTER 7
MEMORY AND STORAGE
LINE NUMBERS AND ADDRESSES : A COMPARISON
When you write a BASIC program, the line numbers you use are
a point of reference. It doesn't matter whether you number your
program 1,2,3,4,5,6, etc. or 10,20,30,40,50, etc. or even
1,10,32,1 13,2000. All you have to do is to make sure that the pro-
gram statements are in order. In fact, the line numbers are simply a
convenient way of ordering your statements so your program
knows what comes next. Thus, the following BASIC programs all
do the same thing despite the different line increments used:
10 PRINT CHR$(147)
20 FOR X = 1 TO 10
30PRINTX
40NEXTX
50 END
5 PRINT CHR$(147)
11 FORX = 1 TO 10
12 PRINT X
130 NEXT X
2000 END
113
1 PRINT CHR$(147) : FOR X = 1 TO 10
3 PRINT X : NEXT X
5 END
Not only can we use any ordered (from lower to higher) line
numbering system, we can put several statements in the same line.
Your BASIC interpreter handles all the details for you. It assigns
the BASIC tokens, knows what is a token and what is a value, and
sticks the program in an area reserved for BASIC programs.
With assembly language programming, the first decision you
make is where to put your program. If you put it in the wrong place,
it'll bomb. For example, if you want to execute your machine pro-
gram from a BASIC program, you'd better not put your machine
code in the area used by BASIC. Likewise there are other areas in
your computer that will conflict with your program, or simply will
not accept the code you enter.
Once you decide where to put your program, you must use con-
tiguous addresses. That means, if your last instruction used ad-
dresses 49152, 49153 and 49154 ($C000-$C002), the next instruc-
tion must begin at 49155 ($C003). You are not using numbers simp-
ly as a way of ordering your commands as in BASIC, you are ac-
tually stuffing information into addresses that will be executed se-
quentially. FORTUNATELY, YOUR ASSEMBLER WILL KEEP
TRACK OF THIS FOR YOU. All you have to do is to make sure
that the area you're using is clear. So while it is imperative to have a
clear spot in RAM to put your program, the addresses used from
that point on will be handled by your assembler.
What confuses most beginning assembly language programmers
is the different ways that various assemblers handle this. Let's look
at the three covered in this book to see how each deals with this.
Kid's Assembler : No Line Numbers
Since this assembler is meant to teach you about what's happen-
ing to your code as you enter it, the actual addresses are given as you
enter your opcodes and operands. The addresses are given in
decimal values so that you can see how many bytes of memory each
114
instruction uses. For example, when using the Kid's Assembler you
would see the following:
ADRS
OPCODE
OPERAND
49152
JSR
$E544
49155
LDA#
49157
TAX
49158
STX
$D021
etc.
In the above example the first instruction used 3 bytes at addresses
49152, 49153 and 49154. Therefore, the next available address is
49155, which you can see in the second line. The next instruction used
only 2 bytes, 49155 and 49156, and the third instruction, only a single
byte. Thus, while the increments from one instruction to the next
may be uneven, you can get a clear idea of where your code is being
stored and how much memory is being used.
Merlin64
When you use the editor in Merlin, you get line numbers in-
cremented by one as you enter your program. This system makes it
easier to keep track of everything, and since you can insert code be-
tween the lines, you don't have to worry about having spaced be-
tween line numbers. With the powerful editing features in Merlin64,
you can manipulate and change your opcodes and operands.
However, until you learn how many bytes each instruction uses in
the different addressing modes, you won't know how much
memory you used until you ASseMble your code. At that time you
will see the actual addresses and the number of bytes you used. You
don't have to worry about addresses, other than where to start plac-
ing your code, since like the Kid's Assembler, this is automatically
done for you.
Commodore Development System
When using EDITOR64, you should treat your line numbers the
same way as you would a BASIC program. This is because you will
need room to insert lines between line numbers. If you run out of
115
room between lines, you can reNUMBER your lines (fortunately)
to insert code. These line numbers, however, are not the addresses
you in which your code is stored. Rather, they simply represent the
sequence in which your instructions will be assembled. Once you
use your ASSEMBLE64 program, you can see the addresses where
the code is stored.
ROM AND RAM MEMORY
If you don't already know it, there's an important difference be-
tween ROM and RAM memory. Basically, ROM memory is "iron
plated" and what is there, stays there. Your BASIC ROM, for ex-
ample, is always the same no matter how many times you turn your
computer on and off. (You can move ROM routines into RAM and
change them in RAM. However, this has no effect on what's in
ROM.) RAM, on the other hand, can be changed simply by enter-
ing new values in the RAM addresses. You might imagine your
computer's memory as a book with only some of the pages with
print. Those pages with print have information that stays on those
pages. It also has some blank pages where different material can be
added or changed. The blank pages are like RAM and the printed
pages like ROM. However, even with the blank pages, there are cer-
tain ones that are reserved for certain things you will usually want
on those pages. For example, while BASIC is in ROM, it loads into
RAM in certain locations. You can change what has been put in
RAM, but usually you leave it alone. That leaves certain other
pages that are almost always blank, and you can write whatever you
want in them. These are the pages we will use to store our assembly
language programs.
Your Commodore 64 can access more than 64,000 RAM ad-
dresses, but we're going to concentrate on basic 64K RAM con-
figuration. Some parts of this RAM are normally used for the in-
formation stored in ROM. Thus, while we will refer to them as
ROM areas, in fact, they can be used as RAM. (Think of these areas
as having ROM routines loaded into them from the ROM chips.)
First, lets look at a "map" of how your Commodore 64 defaults
memory set-up. This will be used in our programming considera-
tions even though some assemblers rearrange memory to provide
more space for machine language programs.
116
STANDARD MEMORY ALLOCATION
$E000-$FFFF
57344-65535
8K Kernal ROM
$D000-$DFFF
53248-57343
4K I/O or
Character ROM
$C000-$CFFF
49152-53247
4KRAM
$A000-$BFFF
40960-49151
BASIC ROM
or ROM Plug-in
$8000-$9FFF
32768-40959
8K RAM
or ROM Plug-in
$4000-$7FFF
16384-32767
16K RAM
$0000-$3FFF
00000-16383
16K RAM
First 32K
At first glance, you might look at those two 16K blocks of RAM
at the bottom and decide that's the place to store your machine
code. As it happens, this is not the case. First of all, you won't be
doing any programs that use 32,000 addresses for a while, and if
you did, you'd run into the area where BASIC is stored as well as all
kind of other things you don't want to crash into.
8K RAM or ROM Plug-in
Right above the second block of RAM is an 8K block that is used
either as RAM or a ROM plug-in. This is a good place to store your
117
programs if you don't use a ROM plug-in. However, you may want
to write programs that others who do use plug-in ROMs will use. If
you store your programs here, they may be over-ridden or conflict
with a plug-in. For example, I have a plug-in utility program called
"Vic-Tree." I use it all the time for easily accessing my disk and
editing BASIC programs. If I have a machine code program in the
area used by the "Vic-Tree", as soon as I initiate the ROM with
SYS 32768, 1 blow out the machine code stored there.
8K BASIC ROM Area
The next area of RAM, $A000-$BFFF, is where your BASIC
ROM loads in. Programs stored here will conflict with your BASIC
operations. Since you will often want to SYS a machine routine
from a BASIC program, any code stored here will be wipe out your
BASIC.
4KRAM
Next, we come to a
nice clean-looking 4K
area of RAM beginning
at $C000 (49152). This
is the area I like to use
the most since there's
plenty of room to write
programs, and it doesn't
conflict with anything
else. It's above the
BASIC ROM area, out
of the way of plug-ins,
and very far away from
where your BASIC in-
struction storage begins
($800/2048).
4K I/O or Character ROM
Leave this area alone for now since your characters are stored
here or used for your I/O.
118
8KKernalROM
This area is a good place to visit with JSR since it has so many
useful subroutines you will want to use. However, if you store your
program here, you'll conflict with the Kernal. Think of this area as
a tool box. You build your program somewhere else but use all the
tools in this area.
Some nooks and crannies
Now you may be thinking all you have is a crummy 4K for your
programs. That's almost true if you want to use your BASIC and
have room for plug-ins. However, there are some other places you
can use while keeping all your other goodies in tact. Probably the
favorite for assembly language programmers is the cassette buffer
from $33C-$3FB (828-1019). That gives you only 192 bytes, but
we're going to be using short routines to get started; so there's more
than enough room here. The only problem is that if you have a
cassette recorder, you'll crash into some routines used by the
cassette. Likewise, there are little places you can find that are un-
used or so little used that you're probably safe. For example, there
are eight bytes available from $334-$33B, Sprites 13-15 from $3CF-
$3FF as well as other little hidey holes. However, its not worth the
bother to even worry about these locations until your programs get
really big. By that time, though, you'll probably want a re-
configuration of memory to use everything taken up by BASIC.
For the time-being there's plenty of room to work in the 4K area
of RAM. In fact, we'll be able to stick dozens of little routines there
simultaneously that we can SYS from BASIC. After that, the 8K
block of RAM beginning at $8000 is available unless you use a
plug-in ROM a lot. (Merlin64 defaults to this area since the
assembler/editor uses $C000. However, there's no problem in hav-
ing your programs executed from the $C000-$CFFF area once
Merlin is out of memory. We'll discuss this more a little later.)
MINI-MONITOR
To look at the contents of your ROM and RAM, monitors are
handy. In previous chapters, I suggested you write your own
119
monitor, and I still think you should. However* to get you started
on your monitor, the following "mini-monitor" examines the con-
tents of your memory for you. Addresses and their contents are
presented in hexadecimal and decimal. In this way, you can see
where you have free RAM and where there is information stored.
Also, as you start learning the hexadecimal machine opcode values,
you can actually read the routines in your memory. In the next sec-
tion we will be discussing the high-byte / low-byte arrangement of
address storage, and this too you will be able to see. Moreover, since
the mini-monitor is written in BASIC, it will be able to examine
itself. Take a look at the code being stored beginning at address
$800 (2048).
MINI-MONITOR
10 PRINT CHR$(147)
20 PRINT "BEGINNING ADDRESS OR {RETURN} FOR
NEXT"
30 INPUT'ADDRESS. PRESS 'Q' TO QUIT ";AD$
40 IF AD$ = "Q" THEN END
50AD = VAL(AD$)
60 FOR K = AD TO AD+ 15: N = K
70HB = INT(N/256)
80 LB = N-INT(N/256)* 256
90FORX=1TO2
100IFX = 1THENN = HB
110IFX = 2THENN = LB
120 N% = INT(N/16) :GOSUB 250
130 N% = N-N%*16:GOSUB 250
140 IFX=1 THEN H1$=HEX$ : HEX$ = ""
150 IF H1$ = "0" THEN H1$ = "00"
160 NEXT
170HEX$=H1$+HEX$
180 HEX$ = "$" + HEX$ :PRINT HEX$;"-";:HEX$ = ""
190 N = PEEK(K)
200 N% = INT(N/16) :GOSUB 250
210 N% = N-N%*16:GOSUB 250
220 PRINT HEX$;" ";K;"-";PEEK(K) : HEX$ = ""
230 NEXT
120
240 AD$= STR$(K) : GOTO 20
250 REM **********************
260 REM CONVERT DECIMAL TO HEX
270 REM **********************
280 HEX$ = HEX$ + CHR$(48 + N% + 7 * ABS(N% > 9))
290 RETURN
You enter your starting address in decimal. Once you've done
that, just press RETURN to look at the next block of memory, or
enter a new starting address. The hexadecimal and corresponding
decimal values are displayed on the same line to help you get ac-
quainted with going from one number system to the next.
Since your mini-monitor simply looks at hex and decimal values,
it isn't much of a monitor. See if you can change it to do some or all
of the following:
1. Move blocks of code from one area to another.
2. Allow starting addresses to be entered as either decimal or hex-
adecimal.
3. Change memory values. (A simple POKE subroutine will do
that.)
4. Display mnemonic opcodes for machine opcode. (This is
tricky since you have to distinguish opcodes from address values,
but it can be done. The trick is in knowing the number of bytes each
opcode uses.)
This can also be used as a hex-decimal conversion program for
converting useful Kernal addresses to decimal so that you can SYS
them from BASIC. In fact, one of the first areas you will want to
explore is from 57344-65535 ($E000-$FFFF).
BACKWARD NUMBERS :
LOW-BYTE / HIGH-BYTE STORAGE
In discussing the numbering systems used in your computer, we
arranged the high-byte and low-byte in the order we read a number.
For example, a common subroutine in your kernal is at location
121
$E544. You've seen this in our example programs, and we'll be us-
ing it a lot more. Broken down into a high-byte and low-byte, it
would look like the following:
HIGH BYTE
$E5
LOW BYTE
$44
However, your Commodore 64, along with other 6502 based
microcomputers, store two-byte numbers in a low-byte / high-byte
configuration. Therefore, in your computer's memory, it would
look like this:
LOW BYTE
$44
HIGH BYTE
$E5
Everything else is in the expected order, but addresses are stored
backwards. For instance, the following instruction,
JSR
$E544
would be stored in three addresses as
$0000 20
$0001 44 -4-Low byte
$C002 E5 ^-High byte
The '20' is the hexadecimal
machine opcode for JSR. It's
just where we would expect it to
be since it is the first in our
assembly instruction and is at
the first address. However, the
second or low byte, 44, of $E544
comes first, followed by the E5.
If we had made our JSR to $22,
our code would be arranged as
follows:
44
122
$C000 20
$C001 22^- Low byte
$C002 00 ■<- High byte
And a JSR to $FF0 would look likes this:
$C000 20
$C001 00 m- Low byte
$C002 FF -*- High byte
So if you see a number like the following:
1A 09
Just switch it so it is:
09 1A
And you get $91A.
BYTES, OPCODES AND ADDRESSING MODES
We've discussed the fact that different opcodes use different
amounts of memory, and the same mnemonic opcodes in different
addressing modes use different numbers of bytes. As we get into the
actual use of opcodes in the next chapter, you will be better able to
see how this works. For now though, we should preview this a bit.
The first thing to remember is that an opcode uses only 1, 2 or 3
bytes of program memory. Let's examine each in terms of bytes
used.
lByte
The most economical opcodes are those using only a single byte.
For example, we've seen RTS. It has no operand, and so it is said to
be an implied opcode. (Implied addressing.)
123
2 Bytes
Opcodes that can only handle 1 byte operands take up two bytes.
One byte is for the opcode and the other for the operand. Any in-
struction that can have a maximum operand value 255 ($FF) is a
two byte instruction. For example, in the immediate mode, LDA
can only have a maximum operand value of 255 ($FF).
3 Bytes
Opcodes whose operand is a non-zero page address have three
bytes. One byte is used for the opcode, one for the low-byte of the
address and one for the high-byte. For example, LDA in the ab-
solute addressing mode uses three bytes.
SUMMARY
As you practice and experiment, the material we have covered
will become easier. In the next several chapters we will be putting to
work the knowledge we have developed. Thus, what has been
abstract will become concrete and give you a new level of
understanding. You will make mistakes, but whereas an uninform-
ed programmer does not understand why he made a mistake, you
will. For example, you may accidentally load your program into
$800 instead of $8000. Since $800 is the beginning of BASIC, any
BASIC program you try to execute after loading a machine
language routine in that location will bomb. Because you now know
something about how memory works, you will be able to spot the
problem and correct it.
124
CHAPTER 8
JUMPING IN
WHERE TO STICK YOUR PROGRAMS
Just in case you skipped our discussion of program placement,
we'll quickly review both the procedure for assigning the starting
address and the recommended places to start. Since each assembler
is a little different in this regard, read the section that applies to the
assembler you're using.
Auto-Placement With Kids' Assembler
If you're using the Kids' Assembler, the default starting address
is 49152 ($C000.) When you enter the editor/assembler all you
have to do is press RETURN, and your program is stored beginning
at 49152. If you want another address, just enter the decimal value
for the address and press RETURN.
ORG Pseudo-Opcode In Merlin64
If you do not specify a beginning address with ORG in your first
line; then it will default to $8000 (32768). This is fine if you do not
use plug in ROMs, and you can test your programs while Merlin is
still in memory if you use this address. However, if you want your
125
programs in $C000 or some other area outside of the plug-in ROM
location, then use the following format:
LABEL OPCODE OPERAND ;COMMENT
ORG $C®00
This should be your first line of code in the programs we will be
covering in this book. Once you're more advanced you can do more
with ORG in different places in your program.
Commodore Assembler's * = function
If you use the Commodore EDITOR64, your first line should be
* = to some clear area of memory. Since the default address is zero
($0000), you must begin your programs with the * = .
LABEL OPCODE OPERAND ;COMMENT
* = $C000
Since the Commodore 64 Macro Assembler Development
System uses special loader programs to put your assembled code in-
to memory, be careful in choosing your loader. Since our examples
will stick with the 4K of RAM beginning at $C000 (49152), you
should use the LOLOADER. It loads at $0800 . However, since the
HILOADER loads at $C800 , which will be above our example pro-
grams, you can use it as well.
One final thing about the Commodore editor/assembler; you
must end your programs with .END in the OPCODE field. Neither
the Kids' Assembler or Merlin's Apprentice understands this
pseudo-opcode; so only use it with the Commodore system.
VISITING BUILT-IN SUBROUTINES WITH JSR.
At the top of your memory is the Commodore-64 Kernal. A visit
to the Kernal with the JSR instruction will execute the subroutine at
the addresses specified in the operand and return to your program.
For example, one subroutine we'll want to use is called CHROUT.
126
It outputs a character to a specified channel, usually the screen. The
CHROUT routine is located at $FFD2 (65490). Likewise, at $E716
(58159), there is a subroutine to output the contents of the A register
(accumulator) to the screen. Both subroutines can do the same
thing, but each requires different preparation routines we will learn.
In Appendix D there is an extended listing of Kernal subroutines.
As we go along, we'll introduce some other handy subroutines that
are built into your Commodore-64.
The best way to envision the JSR instruction is as a GOSUB.
However, unlike BASIC where you must write your own routines,
the JSR goes to a built-in subroutine. (Of course you can JSR to
subroutines you've written yourself, but that will come when you're
more advanced.) For the time being, think of the JSR in terms of
built-in subroutines that do all kinds of things for you. In fact, the
JSR instruction is actually simpler than GOSUB since with BASIC
GOSUBs you have to prepare your own subroutine. Just imagine a
really nice person at Commodore who spent a lot of time writing
complex subroutines for you to make assembly language program-
ming easier. The following is a sampling of some handy subroutines
to visit with JSR:
HEX DECIMAL FUNCTION
$E544
58692
$E566
58726
$E716
59159
$E891
59537
$E8E7
59624
$E9FF
59903
$FF9F
65439
$FFCF
65487
$FFD2
65490
$FFE4
65508
$FFF0 65520
Clear screen
Home cursor
Output to screen
Perform RETURN
Scroll screen
Clear screen line
SCNKEY-scan keyboard
CHRIN-get char from input
channel, usually keyboard
CHROUT-output a character
GETIN-get a character, usually
from keyboard
PLOT-set cursor location
Most of the built-in subroutines expect some value in the ac-
cumulator or some preparatory set of instructions. For example, a
127
JSR to $E516 (output to screen) sends whatever is in the A register
(accumulator) to the screen. Other subroutines, such as CHROUT
expects a channel defined by the CHKOUT subroutine, and in turn,
CHKOUT expects the OPEN subroutine to prepare the channel.
We'll get to these as we accumulate more instructions.
To get you going, we'll do some simple JSR's to subroutines ex-
pecting nothing. However, before we do that, we'd better look at
RTS.
GETTING OUT WITH RTS
Probably the most frequent cause of Computer "lock-up" is the
failure to end a routine with RTS. If you execute a machine
language program from BASIC in either the immediate mode or
from a program with SYS, control is taken over by your machine
language program. The program executes instructions one address
at a time. If there is not an instruction to tell the program to ReTurn
from Subroutine (RTS) then it merrily goes on to the next address
and instruction. This can be a real problem if the area where you're
programming has "garbage" in it, either in the form of the rem-
nants of another machine language program or other code that
somehow got placed there. For example, the following might be a
simple routine to clear the screen that forgot to include an RTS:
$C000JSR $E544
Garbage from here on
$C003 LDA #0 ^- Remnants from other program
$C005STA $D021
$C008LDA #144
$C00AJSR $E716
More junk...
Your JSR to $E544 sure cleared the screen for you, but the "gar-
bage" turned your background and character color to black. So in-
stead of having a nice clear screen and control over your computer,
you have black on black with no visible cursor!
Now that we have JSR and RTS, let's write some simple pro-
grams:
128
LABEUADRS OPCODE OPERAND ;COMMENT
ORG $C000 ;Merlin only
* = $C000 ;Commodore only
49152 JSR $E544
49155 RTS
.END ; Commodore only.
After entering RTS on the Kids'
Assembler, enter 'Q' for 'quit.'
As our listings become more
complex, we will have separate
listings for the Kids' Assembler
and a general listing for other
assemblers. You will have to
remember to include the ORG
or *= pseudo-opcodes (or
whatever your assembler uses to
indicate the start address of your
program) and to end it with
.END on your Commodore
assembler.
With RTS, dear.
How do we
^get back
Ma?
Now, you've seen that little program elsewhere in this book, and
by now you should not only know how to clear your screen, but
how to get back control of your computer once you've done that.
Just to see if you understand the concept of JSR, see if you can
write a program that will Home the cursor but not clear the screen.
(HINT: Find the address of the Home cursor routine.) If you can
do that, go on to the next section.
LOADING UP WITH LDA
Your accumulator is the work horse of assembly language pro-
gramming. Its contents, which can be from $0-$FF (0-255), are
used by other instructions. Furthermore, many of the built-in
subroutines expect something in the accumulator. One way to get
something into the accumulator is with the LDA (LoaD Ac-
cumulator). For example, try the following little program:
129
LABEL
GENERAL
OPCODE OPERAND COMMENT
ORG $C000
; Merlin
Assembler
* = $C000
; Commodore
Assembler
JSR $E544
; Clear screen
LDA #88
; Load the ac-
cumulator with
the decimal
value 88
JSR $E716
; Output to
screen
RTS
.END
; Commodore
assembler
KIDS' ASSEMBLER
ADRS
OPCODE OPERAND
49152
JSR $E544
49155
LDA# 88
49157
JSR $E716
49160
RTS
Note the the presence of the pound (#) sign right before the 88 (or
at the end of the LDA in the Kids' Assembler.) That means the
LDA is in the Immediate mode. The value in the operand is actually
loaded into the accumulator.
We'll discuss addressing modes more in a bit, but let's see what
the program does. When you assemble and run the program, you
will see an 'X' printed in the upper left hand corner of your screen.
The "output to screen" subroutine at $E716 took the value in the
accumulator, and placed it on the screen. Since the 88 is the ASCII
value of the letter X, that's what was output to the screen. Appen-
130
dix J has a complete listing of the various values for your Com-
modore Character Set.
The following example loads different values into the ac-
cumulator and prints my first name, "BILL". (Remember the
ORG on Merlin and *= and .END on your Commodore
Assemblers respectively.)
GENERAL
LABEL
OPCODE OPERAND COMMENT
JSR
LDA
JSR
LDA
JSR
LDA
JSR
JSR
RTS
$E544
#66
$E716
#73
$E716
#76
$E716
$E716
B
KIDS' ASSEMBLER
ADRS
OPCODE
OPERAND
49152
JSR
$E544
49155
LDA#
66
49157
JSR
$E716
49160
LDA#
73
49162
JSR
$E716
49165
LDA#
76
49167
JSR
$E716
49170
JSR
$E716
49173
RTS
131
Now wait a minute! There are
only three LDA's and yet the
word BILL has four letters.
What's going on? There is
nothing in the program that af-
fects the contents of the ac-
cumulator. Some subroutines or
other actions can scramble the
accumulator, but generally what Loading the accumulator lda
was last loaded with LDA stays
there. Therefore, since BILL has two L's together, the value for the
letter L (76) was still there after the first one had been output to the
screen. To see if you understand everything so far, see if you can
change the program to write your own name to the screen.
IMPLIED, IMMEDIATE AND
ABSOLUTE ADDRESSING MODES
The program that printed a letter to your screen used three ad-
dressing modes, implied, immediate and absolute. The implied
mode occurs when no operand exists. RTS is in the implied address-
ing mode. Instructions in the implied mode only take one byte of
memory since all it needs is a single machine opcode.
The immediate addressing mode is signaled by the pound sign
(#), usually the first character in the operand. (As explained in
Chapter 2, the Kids' Assembler attaches the addressing mode to the
end of the opcode.) This means that the contents of the operand are
loaded, stored, compared or in some other way acted upon by the
operand. Thus, LDA #88 (or LDA# 88), took the value of the
operand and put it in the accumulator. Operations in the immediate
mode take two bytes; one for the opcode and one for the operand,
which can be no larger than $FF (255).
Finally, we used the absolute addressing mode with our JSR in-
struction. The absolute addressing mode refers to the address in the
operand, not its value. Since JSR only works in the absolute mode,
we don't have to concern ourselves with it. However, LDA also has
an absolute mode. If we had written,
132
LDA88
instead of loading the accumulator with the value 88, it would have
loaded the accumulator with the value stored in address 88 ($58). If
you want to see the difference in results, just remove the pound (#)
sign from the operand (or the opcode in the Kids' Assembler.) Since
locations 87-96 ($57-$60) are a miscellaneous numerical storage
area, there's no telling what you'll get. (Go ahead and try it just for
fun.)
To summarize the addressing modes we've used so far, keep the
following in mind:
IMPLIED MODE. Uses single byte and no operand.
IMMEDIATE MODE. Uses actual value in operand, either
decimal or hexadecimal, and uses two bytes.
ABSOLUTE MODE. Uses value in the address in operand.
Three bytes of memory are used; one for the opcode, and one
apiece for the low and high bytes of the address.
= = A COMMON MISTAKE = =
Sooner or later you will make the mistake of mixing up
the immediate and absolute modes. You will put in LDA
200 when you meant to write LDA #200 (LDA# 200 on
the Kids' Assembler.) Don't worry, it'll happen, believe
me. Now that you know it will happen, you also know a
common bug in assembly language programs. All you
have to do is to add the pound (#) sign and your results
will be what you expected in the first place. (Also, you'll
wish you had an assembler package with a good editor!)
At this point, we're starting to get somewhere. We've used
assembly language commands interactively. That is, what we did
with one opcode instruction, affected another. That wasn't too dif-
ficult, was it?
133
STORING WITH STA
In BASIC programming you define variables with statements
such as;
A = 45
In essence, you are storing the value 45 in a variable called A. In
your machine language interpreted BASIC program, the variable A
is an address in memory; so that whenever A is accessed, the con-
tents of the address for A is accessed.
In assembly language programming, you actually put a value into
a certain address. Sometimes you will use an address that you know
is empty and is simply a handy place to store a value to be used later
in your program. Very often, though, you will store a value in an
address that activates a process. This will either supply a value for a
built-in subroutine or an address that stores a color or character on
your screen. Let's look at each of these uses with the STA instruc-
tion.
In the absolute mode STA stores the accumulator's value in a
specified address. For example, if you LDA with a value of 10, and
then STA $33C, the value 10 will then be stored in location $33C.
Storage in Empty, Unused RAM
In planning an assembly language program, you not only have to
plan for space for your program, you also have to plan for space for
your variables. For example, if your program uses 30 bytes for the
program itself, it may use another 30 bytes, or even more, for
variable storage. For example, you may use the addresses from
49152-49181 ($C000-C01D) for your machine instructions and
49200-49230 to store your variables. Therefore, you need to plan
for 60 bytes of memory. The following program loads (LDA) a
value into the accumulator in the immediate addressing mode (#),
stores the value at an unused address (STA), clears the accumulator
by loading it with zero, (LDA #0), and then loads the accumulator
from the absolute mode (LDA) from the address it first stored the
134
value. To show you what it did, it prints the character for the ASCII
code in the accumulator with JSR $E716.
LABEL
GENERAL
OPCODE OPERAND COMMENT
{MERLIN ORG
$C000}
{COMMODORE * =$C000}
JSR
$E544
;CLS
LDA
#$43
; ASCII 'C
STA
$C050
LDA
#$0
; Immediate
Mode
LDA
$C050
; Absolute
Mode
JSR
$E716
RTS
{COMMODORE .END}
ADRS
KIDS' ASSEMBLER
OPCODE OPERAND
49152
49155
49157
49160
49162
49165
49168
JSR
LDA#
STA
LDA#
LDA
JSR
RTS
$E544
$43
$C050
$0
$C050
$E716
In the above example, we used only hexadecimal values in the
operand. This was to show you that both the pound sign (#) and
dollar sign ($) had to be included in the operand in standard
assembler format. Before you run the program, see if you can guess
the character to be printed to the screen.
STORAGE IN 'SOFT-SWrrCH' ADDRESSES
A 'soft-switch* is a switch that is activated by the software. In
your Commodore 64, there are many such locations. For example,
135
the background color of your screen is determined by the value in
53281 ($D021). At the end of Chapter 1, we showed all of the values
to be POKEd into 53281 to give you the desired background color
on your TV or monitor. When you use a soft-switch address to STA
a value, it is the same as POKEing that location in BASIC. For ex-
ample, to turn your background to black, you would STA the ac-
cumulator value in $D021 . The following little program shows you
how:
GENERAL
LABEL OPCODE OPERAND COMMENT
{MERLIN ORG $C000}
{COMMODORE *=$C000}
LDA #$0
STA $D021
RTS
{COMMODORE .END}
KIDS' ASSEMBLER
ADRS OPCODE OPERAND
49152
LDA#
$0
49154
STA
$D021
49157
RTS
There are a lot of pointers, flags and registers that can be treated
as soft-switches, and simply with LDA and STA instructions, you
can change them to what you want. The following is a list of some
soft switches and pointers we will be using: (Later on we will deal
with the many registers that affect your sprites and sound.)
SOFT SWITCHES AND POINTERS
HEX DECIMAL FUNCTION
$2B-$2C
43-44
Start of BASIC
$2D-$2E
45-46
Start of BASIC variables
$286
646
Current character color
136
$28A
650
Repeat all keys if $80
$D020
53280
Border color 0-15 ($0-$F)
$D021
53281
Background color 0, 0-15
($0-$F)
$D022
53282
Background color 1, 0-15
($0-$F)
$D023
53283
Background color 2, 0-15
($0-$F)
$D024
53284
Background color 3, 0-15
($0-$F)
Let's make a simple and practical program. In fact, let's make
two. One will make all your keys repeat, and the other will turn off
your key repeat. We'll store one program at 49152 and the other at
49200. When you SYS 49152, all your keys will repeat, and when
you SYS 49200, the repeat will be turned off. These programs can
be loaded simultaneously while you program in BASIC. You might
want to turn on the repeat function if you're working with
keyboard graphics and turn it off if you're working with text. Save
the first program under the name ON and the second OFF.
LABEL
GENERAL - "ON"
OPCODE OPERAND COMMENT
{MERLIN} ORG $C000
{COMMODORE}* =$C000
LDA #$80
STA $28A
RTS
{COMMODORE} .END
KIDS' ASSEMBLER - "ON'
ADRS OPCODE OPERAND
49152
LDA#
$80
49154
STA
$28A
49157
RTS
137
GENERAL - "OFF"
LABEL OPCODE OPERAND COMMENT
{MERLIN} ORG $C030
{COMMODORE}* = $C030
LDA #$0
STA $28A
RTS
{COMMODORE}.END
KIDS' ASSEMBLER - "OFF"
ADRS OPCODE OPERAND
49200
LDA#
$0
49202
STA
$28A
49205
RTS
When you want to use the programs, first load them into
memory either with LOAD "PRG NAME",8,1 (for Merlin and
Kids' Assembler files) or with the LOLOADER program for files
created with the Commodore ASSEMBLER64. When you want
your keys to repeat, just enter SYS 49152, and when you want to
turn off the repeat function, enter SYS 49200. (Congratulations,
you've just written your first utility programs in assembly
language!)
= = AUTO-LOADING MULTIPLE PROGRAMS = =
When you load more than a single machine language
PRG file from the immediate mode in BASIC, you have
to enter NEW after each load. However, Guy Grotke, in
his book Intermediate Commodore 64, shows a way to
load as many programs as you want with a BASIC loader
program. The essential problem with multi-loading from
a BASIC program is that each time you load a file, the
pointer goes back to the beginning of the BASIC pro-
138
gram. As a result, you get caught in an endless loop since
the program will simply keep re-loading the first pro-
gram. However, since the BASIC program preserves its
variables, you can arrange things so that after loading the
first program, it will go on to load the next one. For ex-
ample, the following programs show how to load the ON
and OFF files saved with Merlin's Apprentice and the
Kids' Assembler:
ON/OFF LOADER - MERLIN
10 IF X = THEN X = 1 : LOAD "ON.O",8,1
20 IF X = 1 THEN X = 2 : LOAD "OFF.O",8,1
ON/OFF LOADER • KIDS'S ASSEMBLER
10 IF X = THEN X = 1 : LOAD "ON 49152",8,1
20 IF X = 1 THEN X = 2 : LOAD "OFF 49200",8,1
What happens is that the first time through the program,
the IF/THEN statement in line 10 is evaluated as 'true'
and so the variable X is incremented by 1 and the first file
is LOADed. After the LOAD, the program does not go
to line 20, but because the pointers were reset by the first
LOAD, it starts all over again. However, because X is
now 1 instead of 0, Line 10 is ignored and the program
proceeds to line 20, evaluates the IF/THEN statement as
true, increments X by 1 and then LOADs the second pro-
gram. By adding more lines and incrementing the X
variable, you can LOAD as many PRG files as you want.
(If you have the Commodore assembler package, you will
have to write and save your programs as PRG files from
one of the MONITOR programs. Otherwise, using
ASSEMBLER64, your programs are saved as SEQ files
and they will have to be loaded with LOLOADER or
HILOADER 'by hand.') With that handy little program,
whenever you want your repeat key utility programs in
memory, just RUN the ON/OFF LOADER.
139
STORAGE ON YOUR SCREEN
A final place to STA values is right on your screen! Your screen
memory 40 x 25 matrix is located from 1024-2023 ($400-$7E7).
Overlaying the screen memory on the same matrix is the Color
Memory from 55296-56295 ($D800-$DBE7). Appendices H and I
provide maps of screen and color addresses to make it easy for you
to see where characters will be stored on the screen. When you store
a character on the screen, you also have to store a color or you
won't be able to see the character. (On the first Commodore 64's it
was unnecessary to store the color to see the characters.) The color
codes are the same as the ones for background and border colors,
0-15 ($0-$F), and the character codes are the ASCII values found in
Appendix J.
f?
1024/55296
To align the character and
color, you use the offset 54272.
By adding the offset to your
screen memory address, you will
have the correct address of your
color under the character. For
example, if you look at the
Screen Memory Map in Appen-
dix H, location 1484 is just
about in the middle of the
screen. Add 54272 + 1484 to
get the correct color address of
55756. If you look at the Color
Memory Map in Appendix I,
you will see that value also to be
about in the middle of your screen. The following shows you the
correspondence between the screen and color memories:
Screen & Color
Storage
2023/56295
MC I
Screen
Color
1024
1025
1026
55296
55297
55298
140
1027
1028
55299
55300
2023
56295
Now, using LDA and STA, let's write a program that will put
something on the screen.
LABEL
GENERAL
OPCODE OPERAND COMMENT
{MERLIN} ORG
$C000
{COMMODORE} *=$C00B
i
JSR
$E544
LDA
#0
; Black
STA
55296
; 1st Color
Address
LDA
#88
STA
1024
; 1st Char
Address
RTS
{COMMODORE} .END
ADRS
KIDS' ASSEMBLER
OPCODE OPERAND
49152
49155
49157
49160
49162
49165
JSR
LDA#
STA
LDA#
STA
RTS
$E544
55296
88
1024
We've used the decimal value 88 before, and when we printed it
to the screen, we got an 'X.' This time, though, we get a 'spade'
character. If you look in Appendix J, the Set 1 character for 88 is
141
the spade. Set 2 is the X. Thus, you learned that when you LDA a
value and JSR $E716, the character printed is Set 2, but if you STA
the value of the accumulator at a screen address, you get the
character from Set 1. The color stored at the corresponding color
address give the character its color, not the background.
SUMMARY
With just a few opcodes, we've already been able to output
characters to the screen and even write a little utility program in
assembly language. The JSR, RTS, LDA, and STA opcodes are
heavily used in all assembly language programing, and so you're off
to a roaring start. We also learned about three different addressing
modes, and so in addition to the four opcodes, actually have an ad-
ditional opcode since we learned to use LDA in the immediate and
absolute modes.
In the next chapter, we going to learn some new opcodes that will
add power to your programming. Right now, what we're doing is
equivalent to using a huge number of PRINT and POKE state-
ments in BASIC. However, since we can access built-in subroutines
with JSR, we're actually further along in assembly programming
than we would be in learning BASIC. By taking each opcode a step
at a time, we are able to do a lot with a little; so be patient and forge
ahead!
142
CHAPTER 9
USING THE X AND Y REGISTERS
HOW TO USE TO X AND Y REGISTERS
In addition to your A register, you have two more heavily used
registers called X and Y. Like the opcodes referring to the A
registers that have an 'A' in their name, such as LDA and STA, op-
codes with X and Y refer to the X and Y registers. For example, to
load the X register, the opcode LDX is used. (Come on now, what
would the LoaD the Y register be?)
There are many uses of the X and Y registers, as there are for the
accumulator. Some subroutines read the X and Y registers to
calculate certain results. We saw in the last chapter how the "output
to screen" subroutine at $E716 took the ASCII value stored in the
accumlator and printed a character to the screen. Likewise the
PLOT subroutine at $FFF0 reads the X and Y registers to plot the
row and column position of the cursor. For example, if we wanted
to move the cursor to the middle of the screen on a 40 by 25 matrix,
we would want to specify a location of about 12/19. Let's see how
we can use the X and Y registers to output the contents of the A
register to the middle of the screen.
143
GENERAL
LABEL
OPCODE OPERANDCOMMENT
{MERLIN ORG
{COMMODORE *=$C000}
JSR
$E544
LDX
#12
; Row number
LDY
#19
; Column
number
CLC
; Clear the C
flag
JSR
$FFR)
; PLOT
subroutine
LDA
#88
JSR
$E716
; Output to
screen
RTS
{COMMODORE .END}
KIDS' ASSEMBLER
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
12
49157
LDY#
19
49159
CLC
49160
JSR
$FFF0
49163
LDA#
88
49165
JSR
$E716
49168
RTS
First of all, we did something sneaky! We included an opcode not
yet covered, CLC. The CLC instruction CLears the Carry flag. The
reason we did that is because the PLOT subroutine at $FFF0 can be
used to either read the X/Y position of the cursor or to set it. If the
Carry flag is set; then it reads the X/Y position of the cursor and
144
store it in the X and Y registers. To set the position, the Carry flag
must be cleared. We did that with CLC.
Also notice where we used our LDA. We wanted to load the A
register after the JSR to the PLOT subroutine. The reason for this is
that the PLOT subroutine scrambles the A register. It doesn't do
this to make life difficult for the programmer, but rather the
subroutine itself uses the accumulator. Therefore if we LDA the ac-
cumulator with our 88, to get an 'X' printed to our position PLOT-
ted, after the JSR to $FFF0, we get what we want.
The values for the X register with the PLOT subroutine at $FFF0
can be from 0-24 and the Y values from 0-79. Since the screen is
made up of 25 rows (0-24) the X register values make a lot of sense.
However, we know our screen has only 40 columns (0-39); so what
happens when we have Y values from 40-79? I'm not going to tell
you. You'll have to change the Y value in the above program to see
for yourself!
In addition to using the LDX and LDY instructions in the im-
mediate addressing mode, it is also possible to use them in the ab-
solute mode, just as we did with the LDA instruction. In fact, most
of the same addressing modes of the LDA instruction also apply to
the LDX and LDY instructions, but there are important exceptions
we will see in a bit.
TRANSFERS WITH TAX, TAY, TXA and TYA
Transferring information be-
tween the A register (accumu-
lator) and the X and Y registers
is handled by single byte instruc-
tions. In looking at and remem-
bering how to use TAX, TAY,
TXA and TYA, remember you
Transfer From-To.
T from A to X: TAX
T from A to Y: TAY
T from X to A: TXA
T from Y to A: TYA
TAX
145
If we have the value 22 in the accumulator and issue a TAX in-
struction, the contents of the accumulator are transferred to the X
register. Now both the A register and the X registers would contain
the value 22. None of the Transfer instructions affect the contents
of the register from which the contents were transferred. In fact,
rather than actually transferring contents, the Transfer instructions
duplicate the contents in the target register. Therefore, you might
want to think of the Transfer instructions as Duplicate instructions.
Now, suppose you want to transfer the contents of the X register
into the Y register. There is no TXY opcode. If you really think
about it, though, I'll bet you can guess how to do it. (Think for a bit
before going on. Think. Think. Think.) Okay, time's up. If you
guessed you would transfer the X register to the accumulator with
TXA and then using TAY, transfer the contents of the accumulator
to the Y register, you're absolutely right. Thus, you will often see
the following:
TXA
TAY
Likewise, you can transfer the Y register to the X register using the
TYA-TAX sequence. Let's take a look at a program that will show
you some work with these registers. See if you can guess what will
be printed on your screen before you SYS your program.
GENERAL
LABEL OPCODE OPERAND COMMENT
{MERLIN ORG $C000}
{COMMODORE *=$C000}
JSR
$E544
LDX
#$54
TXA
JSR
$E716
LDX
#$41
TXA
STA
49200
146
LDY 49200
TYA
JSR $E716
LDY #$58
TYA
JSR $E716
RTS
{COMMODORE
.END}
KIDS' ASSEMBLER
ADRS
OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
$54
49157
TXA
49158
JSR
$E716
49161
LDX#
$41
49163
TXA
49164
STA
49200
49167
LDY
49200
49170
TYA
49171
JSR
$E716
49174
LDY#
$58
49176
TYA
49177
JSR
$E716
49180
RTS
If you guessed the message is what transfers the contents of the
accumulator to the X register, you got it right! We did a lot of un-
necessary transferring, but it was more to help you see what's going
on in your computer than an example of efficient programming.
Let's see what happened.
After clearing the screen, the X register was loaded in the im-
mediate mode with $54 (84 decimal). That value was transferred to
the accumulator and then output to the screen. Thus, we got our
T.'
147
After that, we loaded the X register with $41 (65 decimal) and
transferred it to the A register. Then the value was stored in address
49200 with the STA instruction. From the absolute mode we load-
ed the value in 49200 into the Y register, and then transferred it to
the A register and printed it to the screen. That's where we got the
'A.'
Finally, the Y register was loaded with $58 (88 decimal), transfer-
red to the accumulator with TYA and the output to the screen. This
was how the 'X' was produced.
At this point, it might seem that the transfer instructions do little
more than complicate an otherwise simple process; however, as we
go on, we will see how they can be very useful in programming. Our
above example was just to give you some practice in using them.
INCREMENTING AND DECREMENTING
WITH INX, INY, DEX AND DEY.
The incrementing and decrementing of the X and Y registers will
play a crucial role in your programming later on. Here, we're only
going to see what happens when the INX, INY, DEX or DEY in-
struction occurs. Basically, when an INX or INY instruction is
issued, + 1 is added to the X or Y register. With a DEX or DEY in-
struction, 1 is subtracted from the X or Y register. By transferring
the values of the X and Y registers to the accumulator and sending
the results to the screen, we can graphically see what happens.
GENERAL - DEX
LABEL OPCODE OPERAND COMMENT
{MERLIN ORG $C000}
{COMMODORE *=$C000}
JSR
$E544
LDX
#90
LDY
#65
TXA
148
JSR
$E716
; Output to
screen
TYA
JSR
$E716
DEX
; Subtract 1
from X register
TXA
JSR
$E716
INY
; Add 1 to Y
register
TYA
JSR
$E716
RTS
{COMMODORE .END}
KIDS' ASSEMBLER - DEX
ADRS
OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
90
49157
LDY#
65
49159
TXA
49160
JSR
$E716
49163
TYA
49164
JSR
$E716
49167
DEX
49168
TXA
49169
JSR
$E716
49172
INY
49173
TYA
49174
JSR
$E716
49177
RTS
You should have gotten the following on the upper left-hand cor-
ner of your screen:
ZAYB
149
The 90 is ASCII value for 'Z' and the 65, the value for 'A'. Since
the X register was decremented, the next value would be 89, the
ASCII value for the letter *Y\ Conversely, since the Y register was
incremented, it went from 65 to 66 to produce the 'B'.
FROM X AND Y TO MEMORY WITH SIX AND STY
Rather than transferring the X and Y register values to the ac-
cumulator, and then using STA, it is possible to directly store in
memory with STX and STY. The three-byte instructions work in
exactly the same way as STA except instead of storing the A
register's contents, the X or Y register's contents are stored in the
address specified in the operand. Thus, if you program,
STY $C100
the contents of Y are stored in address $C100.
Let's do something useful with our new knowledge. We'll write
another utility program. This one will allow you to link two BASIC
files together. As you know, as soon as you LOAD a BASIC pro-
gram into memory, the current one is replaced by the one just
LOADed. This is a real pain in the neck if you have a lot of
subroutines you'd like to append to a program you're working on.
For example, let's say that you have a handy sort routine stored in a
file named SORT. The program you're working on in memory
needs that program, but since you can't LOAD it without knocking
out your current program, you have key in the whole darned SORT
routine from scratch. Wouldn't it be nice if you could just append
the SORT routine without having to re-key it in? In fact, wouldn't it
be great if you could store all of your handy subroutines in separate
files, and just load them up into the file in memory? In that way you
could cut your BASIC programming time down considerably.
The next two assembly programs will allow you to LOAD a
BASIC file into memory and not destroy the contents of memory.
It does this by tricking BASIC into believing that the LOAD ad-
dress of the second program is the beginning of BASIC RAM. Ac-
tually, it loads the program onto the end of the program in
150
memory. This is done simply by resetting the pointers showing the
beginning of the BASIC program to the end of the program in
memory. Then, a second machine language program resets the
pointers to the actual beginning of BASIC thereby linking the two
programs together. The only constraint on these two little utilities is
that the second and subsequent programs you LOAD have to have
higher line numbers than the ones they're loaded on top of.
GENERAL - APPEND
LABEL
OPCODE OPERAND COMMENT
{MERLIN ORG
$C000}
{COMMODORE *=$C000:
\
LDX
$2D
DEX
DEX
STX
$2B
LDX
$2E
STX
$2C
RTS
{COMMODORE .END}
GENERAL -
LINK
LABEL OPCODE
OPERAND COMMENT
{MERLIN ORG
{COMMODORE *=$C030}
$C030}
; Note address
change
LDA #1
STA $2B
LDA #8
STA $2C
RTS
{COMMODORE .END}
151
MULTI-LOADER PROGRAM (Merlin)
10 IF X = THEN X = 1 : LOAD "APPEND.O",8,1
20 IFX = 1 THEN X = 2 : LOAD "LINK.O",8,1
Since the addresses $2B-$2E are zero-page addresses, we need
special opcodes on the Kids' Assembler. The "-Z" on the end of an
opcode indicates a zero-page operation. Only two bytes are used in-
stead of three. The Merlin and Commodore assemblers
automatically recognize zero-page addresses and do not require
special indicators.
KIDS' ASSEMBLER - APPEND
ADRS
OPCODE OPERAND
49152
LDX-Z
$2D
49154
DEX
49155
DEX
49156
STX-Z
$2B
49158
LDX-Z
$2E
49160
STX-Z
$2C
49162
RTS
KTOS' ASSEMBLER - LINK
ADRS
OPCODE OPERAND
49200
LDA#1
49202
STA-Z
$2B
49204
LDA#
8
49206
STA-Z
$2C
49208
RTS
Note start adrs
MULTI-LOADER PROGRAM (Kids' Assembler)
10IFX = 0THENX = 1: LOAD "APPEND 49152",8,1
20 IF X = 1 THEN X = 2 : LOAD "LINK 49200",8,1
152
To see how to use your new utilities, enter the following two
BASIC programs, and SAVE each to disk or tape as PART 1 and
PART 2:
PARTI
10 REM PART 1
20 REM LOAD FIRST
PART 2
30 REM PART 2
40 REM APPENDS TO PART 1
After you have a copy of both programs SAVEd, load your two
machine files, APPEND and LINK, with the Multi-loader pro-
gram. Next, LOAD "PART 1" and LIST the program. There
should be just two lines, 10 and 20 . Now, SYS 49152. This will ac-
tivate APPEND, and you can now LOAD "PART 2". When you
LIST the program again, you will see line 10-40 all together as a
single program. When you SYS 49200, the pointers will be reset,
and the two programs will be treated as a single large program.
The nice thing about machine language utility programs is that
they will stay in memory while to work with your BASIC files. You
can even RUN your BASIC programs without hurting them. In
that way, you can load the utilities at the beginning a programming
session and forget about them until they're needed to append and
link BASIC files.
ADDRESSING MODES WITH THE X AND Y REGISTERS
In the next chapter, we will see how important the X and Y
registers are in loops and branches. In fact, that is where their real
utility lies, saving you a lot of time in programming. At this point,
we will simply show you what the various addressing modes using
the X and Y registers do.
We have already seen that the immediate and absolute modes
with LDX and LDY work the same as with LDA. However, we can
153
also do indexed addressing using the X and Y registers as offsets to
our intended address.
INDEXED ABSOLUTE ADDRESSING
Basically, the way indexed addressing works with your programs
is to add the value of the X or Y register to the address in the
operand. For example, look at the following program:
GENERAL
LABEL OPCODE OPERAND COMMENT
LDX#0
TXA
STA
$400,X
; Stores at $400
INX
; Add 1 to X
TXA
STA
$400,X
; Store at $401
KIDS' ASSEMBLER
ADRS
OPCODE OPERAND
49152 LDX#
TXA
STA-X $400
INX
TXA
STA-X $400
The program simply loads the X register with , transfers the to
the accumulator and then stores the in the specified address
($400) plus the value of the X register. So the first value is stored at
$400 + , or $400 . Then the X register is incremented by 1 ; so now
the value of X is 1 (0+1 = 1). That is transferred to the ac-
cumulator and stored in $400 + X. Since X is now 1 , $400 + 1 =
$401. Therefore, even though the address in the operand is still in-
dicated as $400, it is actually $400 + contents of the X register as
far as the computer is concerned. The following is the general for-
mula of what occurs with indexed absolute addressing:
154
Indexed Absolute Addressing
ADDRESS = ADDRESS + VALUE
OF X OR Y REGISTER
To see how we can put this to use, let's pick a location that we can
see incremented. We know the screen addresses 1024-2023
($400-7E7) make up our screen memory and 55296-56295
($D800-DBE7) make up the color memory map. By using indexed
addressing, we can place values in those locations simply by in-
crementing X or Y and storing our values in the starting addresses
offset by the X or Y registers.
GENERAL • INDEXED ADDRESSING
LABEL
OPCODE
OPERAND COMMENT
{Merlin
ORG
$C000}
{Commodore
*=$C000}
JSR
$E544
LDA
#4
STA
$D021
; Background
color
LDA
#1
LDX
#0
STA
$D800,X
;Base color
adrs + X
STA
$400,X
;Base screen
adrs + X
INX
STA
$D800,X
STA
$400,X
INX
STA
D800.X
STA
$400,X
RTS
{Commodore:
.END}
155
KIDS' ASSEMBLER - INDEXED ADDRESSING
ADRS OPCODE OPERAND
49152
JSR
$E544
LDA#
4
STA
$D021
LDA#
1
LDX#
STA-X
$D800
STA-X
$400
I NX
STA-X
$D800
STA-X
$400
I NX
STA-X
$D800
STA-X
$400
RTS
The program clears the screen and turns the background color
purple. The value 'l'is loaded into the accumulator and '0 ' into the
X register. We will not be changing the value in the accumulator,
but simply storing it in the screen and color memory areas so that
we can see what's happening. The first storage location is at the base
addresses of $D800 and $400. The X register is incremented, and
the second STA is in $D80 1 and $40 1 . The X register is incremented
once again, providing indexed addresses of $D802 and $402. Your
screen results should be three white A's in the upper left hand cor-
ner.
With our current set of instructions, we could have provided the
actual addresses ourselves and used a few less instructions.
However, using indexed addressing, it is a little more convenient to
use the X register to increment the base addresses. When we get to
loops, you will really see the use of indexed addressing. Finally, we
could have used the Y register as our index in exactly the same way.
The next two addressing modes are a little hairy and will be in-
troduced here just to show you how they work. They will be more
156
useful as you become more advanced. You might want to skip this
last section and go on to the next chapter.
INDEXED INDIRECT ADDRESSING (X Register Only)
If you ever played pool, you probably know what a "bank shot"
is. Instead of making your shot to send the ball directly into a
pocket, you bounce it off the side first. Indexed indirect addressing
is a type of "bank shot" used to check, set or obtain a value. The
following illustrates the basic concept:
ADDRESS = POINTER STORED IN ZERO PAGE +
X REGISTER VALUE = LOCATION OF ADDRESS
In zero page ($0-FF) a set of
pointers are stored in 2 byte con-
figurations. The low-byte is at
the lower address and the high-
byte is at the higher address. For
example, let's say you have
pointers stored in $FB-$FE
(251-254). They have the follow-
ing contents:
$FB-$FC $AB $C0
$FD-$FE $D0 $C0
Indexed Indirect Shot
The addresses are stored low-byte / high byte, therefore we are ac-
tually looking at $C0AB and $C0D0. Note that the two addresses
are not sequential. They do not have to be, but the pointers are se-
quential. Therefore, since each pointer takes up two bytes, the in-
dexing will have to be in steps of two.
When indexed indirect addressing is used, you either fetch a value
from the pointed-to address or store a value there. For example,
let's say that $C0AB has the value 10 ($0A) and $C0D0 has
20($14). Using the format,
or
LDA ($FB,X) ^-General
(LDA-X) $FB ^-Kids' Assembler
157
where X is the contents of the X register, you would load either the
contents of $C0AB or $C0D0. Let's say the X register is '0'. The
following would occur:
LDA ($FB,X)
$FB + = $FB -<- Get the address from $FB and $FC
$FB = $AB $FC = $C0
Address to get value = $C0AB
Contents of $C0AB = 10
Accumulator is loaded with 10
If the X register is incremented by 1, and indexed indirect ad-
dressing is used, you'd be in trouble. That's because the pointer
would show $FB + 1 to be the beginning of the two byte address.
Thus, your load would be from,
$FC = $C0
$FD = $D0
which points to $D0C0. That address is in the I/O, Color RAM or
Character Generator area where you'd probably not be storing
variables. Instead, you'd want to be sure you indexed by 2. Suppos-
ing the value of the X register is 2, you'd get the following:
LDX#2
LDA ($FB,X)
$FB + 2 = $FD •*- Get the address from $FD and $FE
$FD = $D0 $FE = $C0
Address to get value = $C0D0
Contents of $C0D0 = 20
Accumulator is loaded with 20
158
One of the reasons we won't be using this addressing mode much
in this book is because it relies on a Zero-page address for its
pointers. Since the Kids' Assembler is written in BASIC and all
kinds of BASIC pointers are stored in zero-page, we'd be very
limited in the areas we could use. Also, since we will want to use our
machine language subroutines interactively with BASIC, we could
easily bomb the BASIC program we're working with if we stored
several pointers in zero-page. However, to give you a simple exam-
ple of this type of addressing mode, try the following example:
GENERAL INDEXED INDIRECT ADDRESSING
LABEL
OPCODE OPERANDCOMMENT
{Merlin
ORG $0000}
{Commodore
*=$C000}
JSR $E544
LDY #65
;Load Y with
ASCII 'A'
STY $0100
;Store Y in
$0100
LDA #$00
;Low byte of
target adrs.
STA $FB
;Store in LB
pointer adrs.
LDA #$C1
;High byte of
target adrs.
STA $FC
;Store in HB
pointer adrs.
LDX #$0
LDA ($FB,X)
; Indexed in-
direct LDA
JSR $E716
;Output to
screen
RTS
{Commodore
.END}
159
= = ZERO-PAGE ADDRESSING = =
Zero page addressing has special machine language op-
codes that are not apparent in most assemblers. The STA
instruction in the absolute mode has one opcode for non-
zero page addressing and another for zero page address-
ing. Thus, STA instructions for $100 and higher are in-
terpreted as machine opcode $8D, but for locations of
$0FF and lower, the machine opcode $85 is used. Zero-
page addressing saves one byte compared with non-zero
page addressing since the operand address is one byte
($FF or less.) Since the Kids' Assembler has a one-to-one
correspondence between assembler opcode and machine
opcode, it is necessary to have special opcodes indicating
a zero-page operation, indicated by '-Z' extenders. This
method was used on the Append and Link programs in
this chapter already.
KIDS' ASSEMBLER - INDEXED INDIRECT ADDRESSING
ADRS OPCODE OPERAND
49152
JSR
$E544
LDY#
65
STY
$C100
LDA#
$00
STA-Z
$FB
LDA#
$01
STA-Z
$FC
LDX#
$0
(LDA-X)
$FB
JSR
$E716
RTS
The above program is a pretty weird way to get a crummy 'A'
printed to the screen. However, it shows what must be done to set
up indexed indirect addressing. First, the target address must be
160
given a value to use. Second, the low and high byte of the target ad-
dress must be stored in zero-page. Finally, the X Register must be
given a value. After that is done, indexed indirect addressing is
possible. When you begin using tables of numbers, you can use the
X Register as an indirect pointer to the table. In the meantime, I
wouldn't spend a lot of time trying to use this mode.
INDIRECT INDEXED ADDRESSING (Y Register Only)
The final addressing mode we will discuss in this chapter is in-
direct indexed. Like the indexed indirect, indirect indexed address-
ing uses a zero-page pointer. However, instead of using the X
register, it uses the Y. Also, instead of pointing to a series of ad-
dresses in zero-page, it points to a single address offset by the value
of Y. This addressing mode is much easier to use since it involves
only two bytes of zero-page. The following outlines the mode:
ADDRESS = POINTER STORED IN ZERO PAGE +
X REGISTER VALUE = LOCATION OF ADDRESS
For example, let's say you have pointers stored in $FB-$FC
(251-252). They have the following contents:
$FB-$FC $D1 $C0
Stored in low-byte / high-byte configuration, the base target ad-
dress is $C0D1. In the indirect indexed mode, the actual address
would be $C0D1 + Y Register. Therefore, if the contents of the Y
register were 4, the target address would be $C0D1 + 4 or $C0D5.
For a table using sequential addresses, this method is useful for ac-
cessing those addresses using Y as an offset. The format is,
LDA ($FB),Y ^- General
(LDA-Y) $FB -<- Kids' Assembler
Let's take a look at an example that will take values from three
consecutive two-byte addresses and print them to the screen. When
you execute the program, 'ABC will appear in the upper left hand
corner of your screen.
161
GENERAL INDIRECT INDEXED ADDRESSING
LABEL
OPCODE
OPERANDCOMMENT
{Merlin
ORG
$C000}
{Commodore
*=$C000}
JSR
$E544
LDX
#$D1
;Low byte target
adrs.
STX
$FB
;Low byte
pointer
LDX
#$C0
;High byte
target adrs.
STX
$FC
;High byte
pointer
LDX
#65
;ASCII A'
STX
$C0D1
;Store in first
target adrs.
INX
STX
$C0D3
INX
STX
$C0D5
LDY
#$0
;Set Y to $0
LDA
($FB),Y
JSR
$E716
INY
INY
LDA
($FB),Y
JSR
$E716
INY
INY
LDA
($FB),Y
JSR
$E716
RTS
{Commodore
.END}
162
KIDS' ASSEMBLER - INDIRECT INDEXED ADDRESSING
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
$D1
49157
STX-Z
$FB
49159
LDX#
$C0
49161
STX-Z
$FC
49163
LDX#
65
49165
STX
$C0D1
49168
INX
49169
STX
$C0D3
49172
INX
$C0D3
49173
STX
$C0D5
49176
LDY#
$0
49178
(LDA-Y)
$FB
49180
JSR
$E716
49183
I NY
$FB
49184
INY
$E716
49185
(LDA-Y)
$FB
49187
JSR
$E716
49190
INY
49191
INY
49192
(LDA-Y)
$FB
49194
JSR
$E716
49197
RTS
As we pointed out, you won't be using the indexed indirect and
indirect indexed modes much at first. However, one of the best
ways to learn assembly language programs is to look at other peo-
ple's work. By knowing about these two modes of addressing,
you'll be better able to understand what others are doing. In the
meantime, though, don't worry about them. You won't need them
much at the beginning level.
163
164
CHAPTER 10
LOOPS AND BRANCHES
PROGRAM STRUCTURES
At the first mention of program structure, a lot of people groan
that it's enough just to get the program done and working without
having to worry about "structured programming." That's true,
and I certainly won't lecture on the merits of structured programm-
ing except insofar as it makes life easier. All I want to do here is to
explain the fundamental structures in all programming. There are
only three!
SEQUENTIAL
So far, all we have dealt with
in assembly programs is the se-
quential arrangement of instruc-
tions. Each instruction is ex-
ecuted one-after-the-other. It's
like climbing a ladder one rung
at a time going in the same direc-
tion.
Sequence
165
LOOPS
A loop is the second struc-
ture. At a certain point in the
program, the program goes
back to an earlier part and does
it all over again. Most loops
have "escape clauses." That
means that after executing the
loop a given number of times or
meeting some other condition,
such as reading a keypress, it ex-
its the loop and goes on to the
next sequential instruction.
You're familiar with the FOR/
NEXT loop in BASIC.
BRANCHES
A branch occurs when the
program can take two or more
courses of action. If one set of
conditions is met, then the pro-
gram jumps to one place in the
Branch
'
1
i^ Decision ^
'
1
Task X
TaskY
'
n
'
program, and if another set of
conditions is met; then it jumps
to another. The IF/THEN
statement and GOTO and GO-
SUB statements in BASIC are
example of branch structures.
166
That's all there is to structure in programming. (Well, almost.)
It's painless, and as we will see, very important to make your job of
programming easier.
HOW BASIC LOGIC WORKS IN ASSEMBLY LANGUAGE
Remember that the fundamental difference between BASIC and
assembly language programming is that you have to provide more
information in assembly language than you do in BASIC. Other-
wise, both use the same structures and logic. We've already seen
how sequential structure works in both types of programming. For
example, to clear the screen and print the letter 'A' to the screen, the
following two structures are used:
Sequence
BASIC
10 PRINT CHR$(147)
20 PRINT "A"
Assembly
JSR $E544
LDA #65
JSR $E716
RTS
There's really not a lot of difference in the amount of work you
have to do for either. However, to print 'ABC to the screen, there's
a big difference in the effort involved in sequential programming:
BASIC
10PRINTCHR$(147)
20 PRINT "ABC"
167
Assembly
JSR $E544
LDA #65
JSR $E716
LDA #66
JSR $E716
LDA #67
JSR $E716
RTS
With math programs there is even a bigger difference in the
amount of work involved, but we'll get to that in the next chapter.
For now, it is enough to see that sequential structures in BASIC
take a lot less programming than in assembly language programm-
ing.
LOOPING TO SAVE PROGRAMMING TIME
Let's say you wanted to print the alphabet to the screen. In
BASIC you could do something like the following:
10 PRINT "A"
20 PRINT "B"
30 PRINT "C"
etc.
However, you'd probably use a loop and enter something like the
following:
10 FOR X = 65 TO 90 : PRINT CHR$(X); : NEXT X
You use the loop structure because it takes a lot less time to program
and does the same thing as separate PRINT statements. In other
words, it's a smarter way to structure your program.
Now, if you can save steps in BASIC using loops, just think of
what you can save in assembly programming! Remember, since you
have to give a lot more instructions to get something done in
168
assembly language, if you could use those same instructions in a
loop, you could do a lot more with less code.
The question is, "How?" The best way to think of a loop in
assembly language programming is as a "branch back." In BASIC
when your program encounters a NEXT statement, it is really tell-
ing the program to "branch back" to the FOR statement. Second-
ly, there must be some kind of comparison or test to exit the loop;
otherwise you'd be stuck in an endless loop. With the BASIC
FOR/NEXT statement, the test is the top of the loop, (i.e., The last
number in the FOR statement.) In summary, the loop would look
as follows:
1. BEGIN LOOP
2. COMPARE FOR MATCH OR NO MATCH
3. IF NO MATCH GO BACK TO BEGINNING OF LOOP
4. OUT OF LOOP
Let's look at that in a BASIC program using a GOTO loop and
IF/THEN comparison to print the alphabet instead of the
FOR/NEXT statements:
1© X = 65 : REM INITIALIZE X
20 PRINT CHR$(X) : REM BEGIN LOOP
30 X = X + 1 : REM INCREMENT X
40 IF X <> 90 THEN GOTO 20 : REM COMPARE AND
LOOP
50 PRINT "ALL DONE"
Now all we need are opcodes to Compare and Branch. For com-
parison, we will use the following three:
Compare OPCODES
CMP Compare value with accumulator
CPX Compare value with X register
CPY Compare value with Y register
Next, we need Branch opcodes.
169
Branch OPCODES
BNE Branch not equal - <>
BEQ Branch if equal - =
Now, let's take a look at the
equivalent assembly program to
the last BASIC one:
GENERAL - ALPHABET LOOP
LABEL
OPCODE OPERAND COMMENT
{Merlin ORG $0000}
{Commodore *=$C000}
LOOP
JSR
$E544
LDX
#65
initialize X
with 65 - ASCII
A
TXA
JSR
$E716
INX
; Increment X
CPX
#91
; Compare X
with 91
BNE
LOOP
; If X <>91
then branch
back to LOOP
RTS
170
KIDS' ASSEMBLER - ALPHABET LOOP
ADRS
OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
65
49157
TXA
49158
JSR
$E716
49161
INX
49162
CPX#
91
49164
BNE
49157
49166
RTS
Now the first thing to notice in the programs is the different ways
in which the branch was used. Generally, assemblers will have pro-
visions for a LABEL field. In that field you can label your lines,
with the label serving as an address. In the Kids' Assembler, since it
has no label field, you have provide the address to the branch.
(That's one of the reasons the Kids' Assembler gives you the ad-
dresses as you program.)
= = A BRANCH TOO FAR= =
At this stage of the game, you won't be likely to try a
branch that leaps more than a few addresses. If you do
branch more than 128 bytes backwards or 127 forward,
you'll get in trouble. All branch instructions are coded as
branch offsets between 0-255 ($0-$FF) in machine code
and not as addresses. That's why the branch instructions
are only two bytes instead of three. There is a trick to
branching further forward or backwards than 127 or
- 128 by inserting a JMP instruction within the range of
your branch. However, you won't need it for the pro-
grams in this book.
At the beginning of this chapter we looked at a program in
assembly language that printed 'ABC to the screen. It took eight
lines of code since we had to keep putting new values in the ac-
171
cumulator with LDA and then jumping to the screen output
routine. In the last program, we printed all 26 letters of the alphabet
also using eight lines. In other words, we were able to do almost
nine times the output with the same amount of code. As you can
see, using the loop structure saved us a lot of time.
INDEXING WITH LOOPS
In the Chapter 9, we examined indexed addressing with the X and
Y registers. Using indexed addressing we saved a little programming
time since we could increment our base address with X or Y and
didn't have to figure it out every time we wanted to use the next ad-
dress. With loops, however, we can really do a lot with indexed ad-
dressing. Take a look at the following assembly program to see how
we can send information to the sequential addresses of the screen
very easily.
LABEL OPCODE OPERANDCOMMENT
{Merlin
ORG $C000}
{Commodore
*=$C000}
JSR $E544
LDX #0
LDY #1
START
TYA
STA 55296,X
TXA
STA 1024.X
INX
CPX #255
BNE START
RTS
{Commodore
.END}
172
ADRS
KIDS' ASSEMBLER
OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
49158
LDY#
1
49161
TYA
49162
STA-X
55296
49165
TXA
49166
STA-X
1024
49169
INX
49170
CPX#
255
49173
BNE
49161
49175
RTS
The loop allowed us to use the base address for the character and
color screen, and indexing by X, store all 255 characters to the
screen. Using the Y value, we did the same thing with the correspon-
ding color addresses on the screen.
The next programs we will examine, show us two things. First,
after running the BASIC version of the program, you will see the in-
credible speed of a machine language program that does exactly the
same thing. Secondly, you will see how we can use several address
bases within a loop to speed things up. Since the A,X and Y
registers can only hold 255, by having offsets from the base ad-
dresses, we can put these offsets in our program to access more than
the base address + 255, indexed by the X register. (Be sure to run
the BASIC program first, so that you can see what is happening on
the screen.)
BASIC - FILL SCREEN
10 PRINT CHR$(147)
20 FOR X = TO 249
30 POKE 55296 + X,X : POKE 55546 + X,X
40 POKE 55796 + X,X : POKE 56046 + X,X
60 POKE 1024 + X,X : POKE 1274 + X,X
70 POKE 1524 + X,X : POKE 1774 + X.X
80 NEXT
173
GENERAL - FILL SCREEN
LABEL
OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore * = $C000}
JSR
$E544
LDX
#0
LOOP TXA
STA
55296.X
;Base color
addrs.
STA
55546.X
STA
55796.X
STA
56046.X
STA
1024.X
;Base screen
addrs.
STA
1274.X
STA
1524.X
STA
1774.X
INX
CPX
#250
;Compare X
value with 250
BNE
LOOP
RTS
{Commodore .END
KIDS' ASSEMBLER - FELL SCREEN
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
49157
TXA
49158
STA-X
55296
49161
STA-X
55546
49164
STA-X
55796
49167
STA-X
56046
49170
STA-X
1024
49173
STA-X
1274
174
49176
STA-X
1524
49179
STA-X
1773
49182
INX
49183
CPX#
250
49185
BNE
49157
49187
RTS
We used the value 250 for our test so that we could use four equal
blocks to fill the 1000 addresses of our screen. Also notice that we
used the same values for our color codes as the character codes.
NESTED LOOPS
In addition to using loops in assembly language just as you can in
BASIC, so too can you use nested loops. Like regular loops, nested
loops can save you a lot of programming time. For example, in our
last program, we had to put four base addresses as offsets for the in-
dexed addressing we were using to fill up the screen. The reason we
had to do that was because of the 255 ($FF) limitation in the X
register. If the X register could hold 1000, we could have been able
to use a single base address for the character and color bases.
Using a slightly different example, we will now look at a way to
fill up your screen with printable characters using nested loops.
Since the JSR $E716 routine outputs to screen in sequential order,
as long as we JSR $E716 1000 times, we can fill the screen. Now we
know that the X and Y registers are limited to 256 (0-255) before
they burp and fall over, but by using both registers and nested loop-
ing, we can get up to 256 x 256 ($FFFF) passes through a loop. Let's
start with a BASIC program so we can leisurely watch it and then
do the same with an assembled machine language program. Notice
how in all cases, the second loop is inside the first loop. (We only
used ASCII 33-127 to output since the others are color changers and
screen scramblers.)
175
BASIC - NESTED LOOP
10 PRINT CHR$(147)
20 FOR Y = TO 9 : REM LOOP1
30 FOR X = 33 TO 127 : REM LOOP2
40 PRINT CHR$(X);
50 NEXT X : REM BRANCH TO LOOP2
60 NEXT Y : REM BRANCH TO LOOP1
GENERAL - NESTED LOOP
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore * =
JSR
$E544
LDY
#0
LOOP1
LDX
#33
LOOP2
TXA
JSR
$E716
I NX
CPX
#127
BNE
LOOP2
INY
CPY
#10
BNE
LOOP1
RTS
KIDS' ASSEMBLER - NESTED LOOP
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDY#
49157
LDX#
33
49159
TXA
49160
JSR
$E716
49163
INX
176
49164
CPX#
127
49166
BNE
49159
49168
INY
49169
CPY#
10
49171
BNE
49157
49173
RTS
You may have noticed the the program was a little slower than
our last one. That was because the JSR $E7 16 takes more time than
a simple STA. The JSR involves executing a subroutine and return-
ing, while the STA is a single machine language instruction.
BRANCHING FORWARD WITH JMP, BEQ AND BNE
As we have seen with our loops, the branching that occurs is all
backwards. You might ask, "Why jump forward? Why not com-
plete a task, either with or without a loop and then go on to the next
part of the program? ' ' The essence of good programming is what's
called a "Top Down" structure. You begin at the beginning and
proceed in an orderly, sequential fashion to the end of the program.
Jumping all over the place is called "spaghetti" programming.
Essentially, structured programming looks like the following:
TASK1
^CHECK^ ALL DONE? (YES/NO)
- NO: GO BACK AND FINISH
- YES: GO ON TO NEXT TASK
TASK 2
^CHECK^ ALL DONE? (YES/NO)
- NO: GO BACK AND FINISH
- YES: GO ON TO NEXT TASK
TASK END
^CHECK^ ALL DONE? (YES/NO)
- NO: GO BACK AND FINISH
-YES: EXIT PROGRAM
177
'\
Unstructured program
Using structured programm-
ing techniques, you can save a
lot of time and have your pro-
grams run better. However,
treat structured programming
techniques as tools and not
religion! The Top-Down struc-
ture is important, but there are
exceptions to its strict interpreta-
tion. Every time you JSR to a
subroutine, you leave the se-
quential path, and there will be
instances where a task is ac-
complished in an unstructured
fashion. Just remember though,
the more structured your pro-
gram the easier it is to write, run
and debug.
1
TASK1
The following situation is a common one in assembly language
programming:
In TASK 1 , your program loops
until the exit condition is met.
Depending on the outcome of
the loop, your program will
branch to Path 1 or Path 2. If it
goes to Path 1 , you do not want
it to go through Path 2 as well.
This is where the JMP instruc-
tion comes in. If your program
is to branch to Path 2, you can
simply JMP, BEQ or BNE to
Loop
Task
the beginning of Path 2. How-
ever, if you take Path 1, you're
probably going to have to take
an unconditional jump using the
JMP instruction. Look at the
following program:
■o
178
In TASK 1, your program loops until the exit condition is met.
Depending on the outcome of the loop, your program will branch
to Path 1 or Path 2. If it goes to Path 1, you do not want it to go
through Path 2 as well. This is where the JMP instruction comes in.
If your program is to branch to Path 2, you can simply JMP, BEQ
or BNE to the beginning of Path 2. However, if you take Path 1,
you're probably going to have to take an unconditional jump using
the JMP instruction. Look at the following program:
LABEL
OPCODE OPERAND COMMENT
LDX
#0
LOOP
INX
CPX
$C200
;Keep looping
until X= con-
tents of $C200
BNE
LOOP
CPX
$8000
;After loop is
X= contents of
$8000?
BEQ
PATH2
;lf equal then
PATH2
PATH1
INX
TXA
JSR
$E716
JMP
END
;Jump over
PATH2 to end
PATH2
DEX
TXA
JSR
$E716
END
RTS
In the above program, locations $C200 and $8000 have variable
values stored. The X register is incremented until it is equal to the
contents of $C200. When X equals that amount, X is compared
with the contents of $8000. If they're equal, the program takes
PATH2; otherwise it takes PATH1. At the end of PATH1, it
JuMPs over PATH2 to the END of the program. (The above pro-
gram is hypothetical to illustrate a point. If you want to run it, pro-
vide values for $C200 and $8000.)
179
In Chapter 12 when we discuss interacting with a program from
the keyboard, you'll be using JMP and BEQ more frequently. In
the meantime, let's look at a little program that uses both BEQ and
JMP.
GENERAL - BEQ AND JMP
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore * = $C000}
START
END
LDX
#65
TXA
JSR
$E716
CMP
#90
BEQ
END
INX
JMP
STAR1
RTS
KIDS' ASSEMBLER
ADRS OPCODE OPERAND
49152
LDX#
65
49154
TXA
49155
JSR
$E716
49158
CMP#
90
All the program does is print the alphabet to your screen by in-
crementing X until it reaches the value 90. We could have done the
same thing with BNE and used less code, but it did allow us to place
the INX after the comparison and branch instructions. The main
point of the program was to illustrate how BEQ and JMP work.
180
SUMMARY
The main point of this chapter was to show you ways to make
assembly language programming easier. Just as in BASIC where
you can cut down considerably on the amount of work you do by
using loop structures, loops in assembly language save you a good
deal of time as well. Perhaps the most important thing to remember
is using loops with X and Y indexed addressing. The base address of
a block of RAM can be accesses with a minimal amount of program
code.
Overall, you can think of the three structures in programming,
sequential, loop and branch, in the same way you would in BASIC.
In solving a programming problem, use the same structural tools
you would in BASIC. The substitution is in the instructions you
give; not the fundamental logic of those structures. In the next
chapter, we will take a look at some additional instructions that can
be used to structure your program and save you time.
181
182
CHAPTER 11
ADDING AND SUBTRACTING
INCREMENTING AND DECREMENTING MEMORY :
INC AND DEC
We saw that we could increase or decrease the value of the X and
Y registers with INX, INY, DEX and DEY. By using the X and Y
registers, we could indirectly increase or decrease the value in
memory. For example, the following program would increment the
contents of location $C100 using the X register:
LABEL
START
OPCODE
OPERAND
LDX
#$0
STX
$C100
INX
CPX
#$FF
BNE
START
RTS
That method works fine, and the same could be done with the Y
register and decrementing values as well. However, there will be oc-
casions when you will want to use the X or Y registers for something
else while incrementing or decrementing values. For example, sup-
pose you want to use the X register for an inside loop, the Y register
183
for an outside loop, incrementing both registers but you wanted to
decrement the values in a series of addresses.
The INC instruction simply increments the value of the target ad-
dress by one in the absolute mode. For example, if location $C100
contained the value 5,
INC $C100
would increment the value in $C100 to 6. Similarly, if DEC had
been used, the value would be decreased by 1 to 4.
To see how INC works, we'll write a program that will run
through the alphabet for us. (Nothing new, but it shows us what's
happening. Wait'll we get to graphics and use these instructions!)
GENERAL - ABSOLUTE INC
LABEL
OPCODE OPERANDCOMMENT
{Merlin ORG $0000}
{Commodore *=$G000}
LDA
#64
STA
$C100
INC
$C100
LDA
$C100
JSR
$E716
CMP
#90
BNE
START
RTS
.END}
KIDS' ASSEMBLER - ABSOLUTE INC
ADRS OPCODE OPERAND
49152
LDA#
64
49154
STA
$C100
49157
INC
$C100
184
49160
LDA
$C100
49163
JSR
$E716
49166
CMP#
90
49168
BNE
49157
49170
RTS
In the above example, we used INC in the absolute mode. Both
INC and DEC opcodes can be used in the indexed addressing mode
as well. For example, if we changed the above program, we could
INCrement a whole series of addresses with $C100, as the base.
Fill with
INC
LABEL
GENERAL - INDEXED INC
OPCODE OPERANDCOMMENT
{Merlin
ORG $C000}
{Commodore
* = $C000}
LDX #0
LDA #64
STA $C100
START
INC $C100,X
LDA $C100,X
JSR $E716
INX
CPX #90
BNE START
RTS
{Commodore
.END}
KIDS' ASSEMBLER - INDEXED INC
ADRS OPCODE OPERAND
49152
49154
49156
LDX#
LDA#
STA
64
$C100
185
49159
INC-X $C100
49162
LDA-X $C100
49165
JSR $E716
49168
I NX
49169
CPX# 90
49171
BNE 49159
49173
RTS
The results of this program may surprise you. At first, you may
have thought you'd get the alphabet printed to the screen and then
stored in sequential addresses from $C100-$C11A (49408-49434).
That would make a nice table, but that's not what we got. Instead,
as you can see from the mess on your screen, after getting the 'A' as
expected, the rest is garbage. The reason for that lies in the fact that
we were changing the addresses and INCrementing each new ad-
dress by 1 . Thus, while we stored the value 64 in $C100 and then in-
cremented it by 1 to get 65 (the 'A' you saw on your screen), we then
INCremented $C101, which was empty (or full of junk) by 1. That
junk was sent to the accumulator and output to the screen. We'll
need some new instructions to get what we need. (I'll bet you're
smart enough to figure out how to get the alphabet stored in a table
without any new instructions, though. Have the Y register help you
out.)
ADDING AND SUBTRACTING IN THE ACCUMULATOR :
ADC AND SBC
The ADC and SBC allow you to ADd to the accumulator with
Carry or SuBtract from the accumulator with Carry. Instead of in-
crementing or decrementing with transfers from the X or Y registers
or memory, you can add and subtract as much as you want in just
about all modes. The statement,
ADC #09
adds + 9 what whatever is in the A register. In the absolute mode,
ADC $C100
186
adds the contents of location $C100 to your accumulator. For ex-
ample, the following program will print 'AZ' to your screen by
adding 25 to the accumulator which already contains 65:
GENERAL - INDEXED ADC
LABEL OPCODE OPERAND COMMENT
{Merlin
ORG
$C000}
{Commodore
* = $CO00}
LDA
#65
JSR
$E716
ADC
#25
JSR
$E716
RTS
{Commodore
.END}
KIDS' ASSEMBLER - INDEXED INC
ADRS OPCODE OPERAND
49152
LDA#
65
49154
JSR
$E716
49157
ADC#
25
49159
JSR
$E716
49162
RTS
USING CLC AND SEC
With a single ADC instruction, we can make giant leaps instead
of relying on a series of INX's, INY's or INC's. However, when we
start to use a series of ADC's, it's necessary to clear the carry flag.
(In fact it's a good idea whenever ADC is used.) To do that, you
simply use the sequence shown in the following example:
CLC
ADC
#$D3
187
The reason for clearing the carry
flag (the C flag - remember NV
SDIZC?) is because the carry
flag may have been set by some
other operation. When you
ADd with Carry, you add the in-
tended number along with the
Carry if it is set. For example, a
JSR to an output routine may
set the carry flag. In the follow-
ing program where the accumu-
lator is incremented by two each
time the program loops, the
CLC instruction makes sure that
the contents of the accumulator
are incremented only by two and
not two plus the carry. If you
want to see the results without
CLC, delete the CLC instruc-
tion and place the START label
in the ADC line.
GENERAL - ADC BY TWO'S
LABEL
OPCODE OPERAND COMMENT
{Merlin ORG $0000}
{Commodore *=$C000}
********************************************
* *
ADC BY TWO'S
* *
********************************************
START
JSR
$E544
LDA
#63
CLC
ADC
#2
JSR
$E716
188
CMP
BNE
RTS
{Commodore .END}
#89
START
KIDS' ASSEMBLER - ADC BY TWO'S
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDA#
63
49157
CLC
49158
ADC#
2
49160
JSR
$E716
49163
CMP#
89
49165
BNE
49157
49167
RTS
= = GETTING FANCY = =
We used a box of stars (asterisks) in our general assembler
program to show you how to give your source code a hot-
shot header. On most assemblers, if the first character is a
semi-colon, the entire line is read as a comment. It's sort
of like having REM statements use entire lines for headers
in BASIC programs. On the Merlin Apprentice assem-
bler, you don't even need the semi-colon. If your first en-
try is CTRL-P, you'll get a line of stars. If you hit the
space bar and press CTRL-P the first and last spaces will
get stars. It helps to have headers so that you can quickly
see what the source code is for. This is especially true if
you're working with a lot of different assembly language
programs. It is also a good idea to put the date in the
header so that you will know when you last worked on the
program. With larger programs, you may make several
versions, and the dates will keep you posted on which ver-
sion you have in the editor.
189
Now, this next part is weird; so put down your root beer and
listen up. When we want to ADC, it's important to clear the carry
flag with CLC. However, when we subtract from the accumulator
with SBC we want the carry flag set; so we use SEC to set the carry
flag. The reason for this is that in subtraction, the set carry flag is
treated as though no borrow is taken; just the reverse of ADC.
What you're really doing is called "two's compliment" addition
since that's the way your 65 10 can best handle subtraction. It sort of
uses the carry flag to "add backwards." Rather than losing sleep
over the process, just remember:
SUBTRACT - SEC
Use the format in the following example:
SEC
SBC #$08
Okay, we're all set to use subtract. We'll go backwards in the
alphabet printing only every third character. In that way you can see
how the process works.
GENERAL - SBC BY THREE'S
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore * = $C000}
********************************************
* *
SBC BY THREE'S
* *
START
JSR
$E544
LDA
#93
SEC
;Set the Carry
SBC
#3
;Subtract 3 from
the accumulator
JSR
$E716
CMP
#66
190
BNE
RTS
{Commodore .END}
START
KIDS' ASSEMBLER - BY THREES
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDA#
93
49157
SEC
49158
SBC#
3
49160
MSR
$E716
49163
CMP#
66
49165
BNE
49157
49167
RTS
SUMMARY
This has been a short chapter, but we learned some important
new instructions for adding and subtracting numbers. We did not
tackle the more difficult problems of dealing with numbers larger
than 255 ($FF) since that gets a little hairy. If you want to add 2 +
2, stick with BASIC for the time being, and the same is even truer
when dealing with multiplication and division. Larger numbers can
be handled in assembly language using two addresses, and when
you become more advanced, you'll learn either how to use JSR's to
BASIC subroutines or how to write your own arithmetic programs.
As you will see, we'll have our hands full just dealing with values a
single register or address can handle.
191
192
CHAPTER 12
INTERACTING WITH
ASSEMBLY LANGUAGE PROGRAMS
Introduction
Up to this point we've been concentrating on the fundamental in-
struction set in 6510 assembly language. Everything we've done has
been "locked" into the program. That is, we have no way outside
of writing the actual program of affecting what course of action the
program will take. When the program is SYSed, it takes a pre-
determined course the user cannot influence while the program is
running. What we've done so far is similar to programming in
BASIC without INPUT or GET statements. In this chapter, we're
going to turn our attention to some simple I/O (input/output)
routines. These will give you some real programming power, and
you won't have to learn any new opcodes.
All commercial software "interacts" with the user. When you
play an arcade game or use a word processor, your input affects
what the program does. For example, in arcade games, the program
takes its input from paddles or joysticks. If you press a button, your
game fires a missile, and if you move the joystick to the left, your
character moves to the left. Similarly, in word processing, your
keyboard is the primary input device. Your program stores the in-
193
formation supplied by the keystrokes, and that information will be
different depending on what keys you hit.
Fortunately, there are several built-in routines for handling I/O,
and while these routines may use some very complicated and
sophisticated code, all you are going to have to do is to know when
to JSR to these subroutines. There are a number of "formulas"
you'll have to learn since jumping to these subroutines can affect
other parts of your program, but these formulas are fairly simple
and direct. Moreover, we will concentrate only on those subroutines
that affect input from the keyboard, joysticks or game paddles. We
will not deal with tape or disk I/O. Since we have already dealt ex-
tensively with output in our examples of printing characters to the
screen, most of this chapter will concentrate on input.
READING INPUT FROM THE KEYBOARD
Have you ever wondered what actually happens when you press a
key? You know that your computer handles everything in binary
configurations, and so if you press the 'K' key, a big 'K' doesn't go
floating into your computer and up to the screen. We'll break down
what happens when you press the letter 'K' to show you the path
from the keyboard to your screen.
1. Scan the keyboard.
2. Get the character from the keyboard and put it in the A
register
3. See if the key is null (no key has been pressed)
4. If the key is null, go back to Step 1 and scan again.
5. Print the character in the A register to the screen.
Since we have sent ASCII characters from the A register to the
screen by storing them on the screen or JSRing to an output routine,
we already know how to do the last part. What we need is a routine
to scan the keyboard and to get it from the keyboard to the ac-
cumulator.
To do that we will use Kernal routines, SCNKEY and GETIN.
The SCNKEY subroutine is located at $FF9F (65439) and the
GETIN routine at $FFE4 (65508). For output, instead of using
194
$E716, we'll use CHROUT (Character Out) at $FFD2 (65490).
Thus, our sequence of subroutines will be:
1. SCNKEY - Scan the keyboard
2. GETIN - Put the key value in A register
3. CHROUT - Output the character to the screen
Knowing that, we can try out a routine that will print characters to
the screen based on keyboard input:
= = LABEL YOUR SUBROUTINES = =
With most assemblers, (not the Kids' Assembler,
though) it is possible to define and label subroutines.
Then when you want to use a subroutine, instead of doing
a JSR to the specific address, the JSR is to the subroutine
label. For example, instead of,
JSR $E544
you can,
JSR CLEAR
This method is more descriptive and easier to remember
than memorizing all the different addresses that contain
the subroutines. Unfortunately the Kids' Assembler does
not have a label field, but most other assemblers do.
GENERAL - KEYBOARD TO SCREEN I/O
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore *=$C000}
{Commodore}
CLEAR =$E544
SCNKEY =$FF9F
195
GETIN
= $FFE4
CHROUT
= $FFD2
{Merlin}
CLEAR
EQU
$E544
SCNKEY
EQU
$FF9F
GETIN
EQU
$FFE4
CHROUT
EQU
$FFD2
JSR
CLEAR
SCAN
JSR
SCNKEY
;Look to see if
key is pressed
JSR
GETIN
;Put key value in
accumulator
BEQ
SCAN
;Compare with
zero
JSR
CHROUT
;lf not zero print
to screen
RTS
{Commodore
.END}
<**
KIDS' A
SSEMBLER
KEYBOARD TO SCREEN I/O +
Scanning Keyboard
ADRS
OPCODE
OPERAND
49152
JSR
$E544
49155
JSR
$FF9F
49158
JSR
$FFE4
49161
BEQ
49155
49163
JSR
$FFD2
49166
RTS
196
The program involves only two instructions, not counting the
RTS. Comparing the code in the Kids' Assembler with the
assemblers using labels, you can see how much clearer it is using
labels. When we start doing more forward jumps and branches, the
labels are almost indispensable. (Be sure to note the different ways
that the Commodore assembler and Merlin handle labels.)
Since the program only printed out a single character before end-
ing, we didn't get a chance to do much. What we need is a program
to keep reading the keyboard until a certain key is pressed to let us
know it's time to quit. We'll write a program that prints the key
pressed to the screen until RETURN is pressed. This will involve a
double comparison.
1. Compare key with null
2. Compare key with RETURN.
When you run this program, be sure to try out your CTRL keys
was well as you regular keys. Turn on the RVS (reverse) and take it
through its paces. (If you're smart and you're using a decent
editor/assembler, instead of rewriting the whole thing from scratch,
you'll just edit the last program to make this one. From this point on,
especially when you're writing your own original programs, you will
begin to see the severe disadvantages of the Kids' Assembler. Instead
of blowing your money on an arcade game, next time you got a few
bucks to spend, get a good assembler.)
GENERAL - READ RETURN
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $0000}
{Commodore *=$C000}
{Commodore}
CLEAR
= $E544
SON KEY
= $FF9F
GETIN
= $FFE4
CHROUT
= $FFD2
197
{Merlin}
CLEAR
EQU
$E544
SCNKEY
EQU
$FF9F
GETIN
EQU
$FFE4
CHROUT
EQU
$FFD2
JSR
CLEAR
SCAN
JSR
SCNKEY
JSR
GETIN
BEQ
SCAN
CMP
#$0D
;Compare with
ASCII for
RETURN (13)
BEQ
END
;Jump to end of
program if
RETURN is
pressed
JSR
CHROUT
JMP
SCAN
^^^.
END
RTS
h->^2h
{Commodore
.END}
.' Label : J^flfc
I Subroutines^ HI
KIDS' ASSEMBLER -
READ RETURN
ADRS
OPCODE OPERAND
49152
JSR
$E544
49155
JSR
$FF9F
49158
JSR
$FFE4
49161
BEQ
49155
49163
CMP#
$0D
49165
BEQ
49173
49167
JSR
$FFD2
49170
JMP
49155
49173
RTS
198
Now we can see how we can have one of two branches using the
keyboard. If we press any key other than RETURN, we'll get the
character or the special effects, such as color change or reverse, on
the screen. We loop through the SCAN very much like we'd use the
GET statement in BASIC.
10 GET A$ : IF A$ = "" THEN 10
We just keep looping until we get something other than a null.
Then, like BASIC, we use the IF/THEN logic to either print the
character and go get another one or quit. The same thing in BASIC
would look like the following:
10 GET A$ : IF A$ = "" THEN 10
20 IF A$ = CHR$(13) THEN END
30 PRINT A$: GOTO 10
Now that we have learned how to use IF/THEN logic with infor-
mation from the keyboard and can branch in more than one direc-
tion, let's use several branches. While we're at it, we'll also supply a
prompt and do something other than just printing characters to the
screen. A simple routine we already have used is changing the
background color on the screen with a JSR to $D021. Instead of
entering the color value, we'll enter a letter value for the color and
have a subroutine supply the color value. (We're really getting hot.)
First of all, how do we make a prompt? Since the prompts tell us
what to do, they're very important in programs. In BASIC, using
the GET statement, we simply use PRINT. For example,
10 PRINT "PRESS A KEY "
20 GET A$ : IF A$ = "" THEN 20
30 PRINT A$: GOTO 10
asks you to PRESS A KEY and then prints it to the screen.
Since we know that whatever is in the accumulator will be printed
to the screen with a JSR to CHROUT ($FFD2) all we have to do is
to load up the accumulator with our prompt and print it to the
199
screen. We're going to change the color of the screen; so we'll use
COLOR? as our prompt.
Once we have the prompt, we can use our SCNKEY and GETIN
subroutines to read the keyboard and put the results in the ac-
cumulator. Now, since we want to change the background color,
we will JSR to $D021. However, the background colors 0-15 re-
quire CTRL keys, and we want to use regular keys. To keep it
relatively simple, we'll just change to background colors to (R)ed or
(G)reen. If 'R' is pressed, the background will turn red and 'G' will
turn it green. Also, to get out of the program, RETURN will cause
an exit. Otherwise, the program will do nothing. Thus, we will have
branches on the following:
1. IF RETURN (ASCII = $©D or 13) is pressed THEN goto
the end of the program.
2. IF an R (ASCII = 82) is pressed THEN put a 2 in loca-
tion $D021.
3. IF an G (ASCII = 71) is pressed THEN put a 5 in loca-
tion $D021.
Using the labels END, RED and GREEN it is very simple to
indicate where our branches are going and what they are do-
ing. This is why the Kids' Assembler is difficult. You have to
figure out the address to branch aheadbefore that address is on
the screen. This means that you have to determine the number of
bytes that will be used in the program lines so that you will have the
right address on a forward branch. Using the LABEL field,
however, all you have to do is to put in the label name in the
operand, and when you get to the subroutine, use that label at the
beginning of the line. (Using the Kids' Assembler, though, you'll
really understand what's going on inside your machine. This will
turn you into a "mean" assembly language programmer and make
programming with a good assembler a snap.)
200
GENERAL - RED/GREEN BACKGROUND
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore *=$C000}
• *
; * RED/GREEN BACKGROUND
. ******************************************
{Commodore}
CLEAR
= $E544
SCNKEY
= $FF9F
GETIN
= $FFE4
CHROUT
= $FFD2
BKGND
= $D021
{Merlin}
CLEAR
EQU
$E544
SCNKEY
EQU
$FF9F
GETIN
EQU
$FFE4
CHROUT
EQU
$FFD2
BKGND
EQU
$D021
JSR
CLEAR
LDA
#67
C
JSR
CHROUT
LDA
#79
JSR
CHROUT
LDA
#76
L
JSR
CHROUT
LDA
#79
JSR
CHROUT
LDA
#82
R
JSR
CHROUT
LDA
#63
?
JSR
CHROUT
LDA
#0
Null the
accumulator
201
SCAN
JSR
SCNKEY
JSR
GETIN
BEQ
SCAN
CMP
#$0D
;Was RETURN
pressed
BEQ
END
;lf so, goto END
CMP
#82
;WasR
pressed?
BEQ
RED
;lf so, goto RED
CMP
#71
;Was G pressed
BEQ
GREEN
;lf so, goto
GREEN
JMP
SCAN
;lf none of the
above, go get
another key
RED
LDA
#2
;Color code for
RED
background
STA
BKGND
JMP
SCAN
GREEN
LDA
#5
;Color code for
GREEN
background
STA
BKGND
JMP
SCAN
;Go get another
key
END
RTS
{Commodore
.END}
KIDS' ASSEMBLER - RED/GREEN BACKGROUND
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDA#
67
49157
JSR
$FFD2
49160
LDA#
79
49162
JSR
$FFD2
49165
LDA#
76
202
49167
JSR
$FFD2
49170
LDA#
79
49172
JSR
$FFD2
49175
LDA#
82
49177
JSR
$FFD2
49180
LDA#
63
49182
JSR
$FFD2
49185
LDA#
49187
JSR
$FF9F
49190
JSR
$FFE4
49193
BEQ
49187
49195
CMP#
$0D
49197
BEQ
49226
49199
CMP#
82
49201
BEQ
49210
49203
CMP#
71
49205
BEQ
49218
49207
JMP
49187
49210
LDA#
2
49212
STA
$D021
49215
JMP
49187
49218
LDA#
5
49220
STA
$D021
49223
JMP
49187
49226
RTS
NOTICE: If you do not have a joystick, you can skip this next
section. However, you may want to go over the part on using
the EOR instruction.
JOYSTICK CONTROL
Using GETIN we can take the value of a keypress and put it in
the accumulator. With the joystick however, we do not have a
SCNKEY and GETIN routine. Therefore, we will have to build our
own. We'll look at reading the joystick in Port 1 and fire button
only. However, Port 2 is read in the same way as Port 1 except from
a different address. Therefore, if you know how to read the joystick
in Port 1, reading it Port 2 is essentially the same. For Port 1, we
203
read the value at address $DC01 (56321) and for Port 2,
$DC00(56320). Let's now look to see what happens when you use
the joystick and the register at $DC01.
Register at $DC01
7 6 5 4 3 2 10 ^-Bit number
XXXFRLDU ^-Switch read
X = Unused by joystick
F = Fire button
R = Joystick right
L = Joystick left
D = Joystick down
U = Joystick up
Not pressed = 1
Pressed =
For example, if the joystick is in a neutral position, all switches
are read as "not pressed." Therefore, the register would look like
the following:
Register at $D<
C01
7 6 5
XXX
4
F
3
R
2
L
1
D
<+- Bit number
U -*- Switch read
1 1 1
1
1
1
1
1 ^- Bit configuration
for neutral
That's simple enough to read since the register is filled up. We know
a single 8 bit register can only hold 255 ($FF) so when the joystick is
in neutral, the value read at $D0C1 is 255 or $FF. Now, let's take a
look at what happens when we move the joystick to the left.
204
Register at $D'
C01
7 6 5
XXX
4
F
3
R
2 1
L D
-*-Bit number
U ^-Switch read
1 1 1
1
1
1
1 <*- Bit configuration
for left.
When we press the joystick to the left, Bit #2 is turned off to give us
the binary value, 111111011. Unless you're a lot better at reading
binary numbers than I am, that's not easy to figure out. The value is
251 ($FB), but I had to do a lot of computations to get it. There's
got to be a simpler way to read the $DC01 register.
The EOR Instruction
To make things easier for ourselves, we're going to learn a new
instruction, EOR. This instruction is the mnemonic for "Exclusive
OR." This instruction compares bits to see the differences. If there
is a difference between two bits, you get a "1", and if there are no
differences, you get "0." This serves to "mask" or "filter" values
so that they can be handled easier. To see how it works, let's take
our last example and see what it looks like EORed with $FF.
Register at $DC01
7 6 5
XXX
4
F
3
R
2
L
1
D
U
^-Bit number
«*- Switch read
1 1 1
1 1 1
1
1
1
1
1
1
1
1
1
-^-Bit configuration
for left.
«*-$FF bit
configuration
10 ^- EORed with $FF
Now that's a lot easier to figure out. Instead of 251, we have a
value of 4. Since bits 7,6 and 5 are not used, we can do relatively
quick translations of binary to decimal or hex:
205
16 8 4 2 1 ^-Multiple if "on."
4 3
2
1
«4-Bit number
F R
L
D
U
1
1
1
1
1 1
1
1
1 ^-$FF
1 ^-EORed value
Looking at the above example, what is the value when the joystick is
to the right? If you think the value after EOR with $FF is 8 then you
are right. If the fire button is pushed, what would the value be? It
would be 16. With the joystick in neutral, we saw that the value was
$FF or 255 - all eight bits have a "1". After EOR with $FF you
would have the following:
Register at $DC01
7
6
5
4
3
2
1
-*- Bit number
X
X
X
F
R
L
D
U
•^-Switch read
11111111 -*• Bit configuration
for left.
11111111 ^-$FFbit
configuration
00 -*-EORed with $FF
Now, neutral is "0", which is a lot easier to remember. .
What happens when your joystick is at an angle, such as up-left?
Well, let's just stick the numbers in and see what we get.
16 8 4 2 1 ^-Multiple if "on."
4 3 2 1 «*-Bit number
F R L D U
110 10
11111 <*-$FF
10 1 ^-EORed value
206
A simple calculation shows that the up-left EORed $FF value to be
5. Thus, we can determine the "angle" values of the joystick in ad-
dition to the up, down, left and right directions.
You don't have to figure out these values every time you sit down
to program, just use the following values as a quick look-up of the
EORed joystick values.
Joystick Values EORed with $FF
• Neutral
1-Up
2 - Down
4 - Left
5 - Up Left
6 ■ Down Left
8 - Right
9 -Upright
10 - Down right
16 - Fire button pressed
The following program will let you see the joystick values on
your screen. Remembering that the neutral position is zero, we'll
have to add 48 to that value to print the ASCII character "0" to the
screen. (ASCII 48 = "0" character.) Thus, as you move your
joystick around you will see the actual EORed number in the
$DC01 register. By using LDA $D0C1 and then EORing the ac-
cumulator value with $FF, we put the joystick value into the ac-
cumulator. Then by using ADC #48 to add the decimal value 48 to
the accumulator and CHROUT, we print it to the screen.
GENERAL - JOYSTICK VALUES
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore * = $C000}
207
. irirwiririririrwwww
. *
WWKWWWWWW1
cwwwwwwwww
*
. *
. *
JOYSTICK VALUES
*
*
. ********************************************
{Commodore}
JSTICK
= $DC01
OFSET
= $C100
CLEAR
= $E544
FIRE
= $C102
CHROUT
= $FFD2
{Merlin}
JSTICK
EQU
$DC01
OFSET
EQU
$C1O0
CLEAR
EQU
$E544
FIRE
EQU
$C102
CHROUT
EQU
$FFD2
JSR
CLEAR
LDA
#$FF
;Value to EOR
STA
OFSET
;offset
LDA
#64
;EORed value of
fire button +
48
STA
FIRE
;Store here for
easy reference
START
LDA
JSTICK
;Read joystick
EOR
OFSET
;EOR with $FF
CLC
ADC
#48
;Add 48
JSR
CHROUT
;Print modified
joystick value to
screen
CMP
FIRE
;ls it the fire
button?
BEQ
END
;lf it is, then
quit
208
END
{Commodore
JMP
RTS
.END}
START
;Go back and
read the
joystick.
KIDS' ASSEMBLER - JOYSTICK VALUES
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDA#
$FF
49157
STA
$C100
49160
LDA#
64
49162
STA
$C102
49165
LDA
$DC01
49168
EOR
$C100
49171
CLC
49172
ADC#
48
49174
JSR
$FFD2
49177
CMP
$C102
49180
BEQ
49185
49182
JMP
49165
49185
RTS
We designed the program so that it would keep printing values to
the screen, and your screen fills up with numbers, scrolling different
values as you change the direction of the stick. To exit the program,
you press the fire button on your joystick.
Now that we can easily read the joystick, let's do something with
it. In the next chapter we'll see how to move graphics and sprites
with the joystick, but for now we'll just use it to change the
background colors. This time, though, we will not use ADC for an
offset to the ASCII code. Instead, we'll see what background colors
are created with the different joystick positions.
209
GENERAL - JOYSTICK COLORS
LABEL
OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore *=$C000}
********************************************
JOYSTICK COLORS
********************************************
{Commodore}
JSTICK
= $DC01
OFSET
= $C100
CLEAR
= $E544
FIRE
= $C1002
{Merlin}
JSTICK
EQU
$DC01
OFSET
EQU
$C100
CLEAR
EQU
$E544
FIRE
EQU
$C102
JSR
CLEAR
LDA
#$FF
;Value to EOR
STA
OFSET
;EOR offset
LDA
#16
;EORed value of
fire button
STA
FIRE
;Store here for
easy reference
START
LDA
JSTICK
;Read joystick
EOR
OFSET
;EOR with $FF
CMP
FIRE
;Fire button
pressed?
210
START
END
{Commodore
LDA
EOR
CMP
BEQ
STA
JMP
RTS
.END}
JSTICK
OFSET
FIRE
END
$D021
START
;Read joystick
OR with $FF
;Fire button
pressed?
;lf it is then quit
;Put joystick
value into
background
color register
KIDS' ASSEMBLER - JOYSTICK COLOR
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDA#
$FF
49157
STA
$C100
49160
LDA#
16
49162
STA
$C102
49165
LDA
$DC01
49168
EOR
$C100
49171
CMP
$C102
49174
BEQ
49182
49176
STA
$D021
49179
JMP
49165
49182
RTS
Again, we used the fire button to exit the program. Unfortunate-
ly, we're left with a black screen. You can change it by using ADC
#1 so that instead of the neutral position being black, it will be
white.
MAKING MESSAGES : ASC and .BYTE
At the beginning of this chapter we discussed creating prompts.
As you may have noticed, it took a lot of code to put a simple pro-
mpt like COLOR? on the screen. With most assemblers, there is an
211
easy way to do it using a pseudo-opcode called ASC, .BYTE or
some similar pseudo-opcode. Since the Kids' Assembler does not
have such codes, we'll write some simple BASIC programs that will
allow you to do make prompts and other messages that can be ac-
cessed with assembly language programs.
The ASC and .BYTE instructions operate something like DATA
statements in BASIC. The label in the line with the ASC or .BYTE
directives serves as the beginning address for the word in the
operand field. For example, the following shows the format for the
Merlin and Commodore assemblers respectively, with the starting
address being MSG:
LABEL OPCODE OPERAND
MSG ASC 'MERLIN'
MSG .BYTE 'COMMODORE'
The string in the operand takes up one byte for each character.
Therefore, if MSG were address 49170, the string 'MERLIN'
would take up 6 addresses (49170-49175) and the string 'COM-
MODORE', 9 addresses, (49170-49178). Using the X register for an
index, simply LDA with MSG indexed by X and print each
character to the screen with JSR CHROUT.
First, we'll look at .BYTE
and ASC on the Commodore
and Merlin assemblers respec-
tively. Both work in the same
way, but to avoid confusion,
each will be used with a similar
but separate example.
Reading ASC& .BYTE
LABEL
COMMODORE - .BYTE
OPCODE OPERAND COMMENT
= $C000
212
********************************************
* *
.BYTE
* *
********************************************
CLEAR
= $E544
CHROUT
=$FFD2
JSR
CLEAR
LDX
#$0
READ
LDA
MSG.X
;Load one
character
JSR
CHROUT
; Print to screen
CPX
#8
;See if it is the
length of
message (0-8)
BEQ
END
;lf it is then end
INX
increment X to
read next
character
JMP
READ
END
RTS
MSG
.BYTE
'COMMODORE'
.END
LABEL
MERLIN - ASC
OPCODE OPERAND COMMENT
ORG $C000
****************************************
ASC
* * * * *
*
*
*
******************************************** #
213
CLEAR
EQU
$E544
CHROUT
EQU
$FFD2
JSR
CLEAR
LDX
#$0
READ
LDA
MSG,X
JSR
CHROUT
CPX
#5
BEQ
END
INX
JMP
READ
END
RTS
MSG
ASC
'MERLIN'
Another way to read messages is with a "termination symbol."
At the end of your message before the closing single quote mark,
place a special symbol that is not likely to be part of the message.
The pound sign (#) is a good one to use. Then, instead of having to
count the characters, you can simply compare the value in the ac-
cumulator with the ASCII value for the termination symbol. For
example, instead of having,
ASC 'MERLIN'
we would put,
ASC 'MERLIN*'
In that way when ASCII 35, the value for the pound sign (#) is load-
ed into the accumulator, your program branches out of the
READ/PRINT routine. Take a look at the following example on
Merlin to see how it works.
MERLIN - ASC RELATIVE
LABEL OPCODE OPERAND COMMENT
ORG $C00O
214
*********************************************
*
*
ASC RELATIVE
*
*
*********************************************
CLEAR
EQU
$E544
CHROUT
EQU
$FFD2
JSR
CLEAR
LDX
#$0
READ
LDA
MSG,X
;Load one
character from
MSG
CMP
#35
;ls it the pound
sign yet?
BEQ
END
;lf so then end.
v
JSR
CHROUT
INX
JMP
READ
END
RTS
MSG
ASC
'MERLIN#'
Using this method! you can easily create prompts and other
messages to be output to the screen. Whenever you need the
message, simply read it into the accumulator and throw it out to the
screen. Using several descriptive labels, different messages can be
accessed and printed depending on where your program branches.
MESSAGE MAKER FOR KIDS' ASSEMBLER
It's a pain in the neck to do a mile of LDA#'s and JSR $FFD2's
on the Kids' Assembler to get a message out. Since the Kids'
Assembler doesn't have ASC or .BYTE, the following BASIC pro-
gram gives you a way to stick your message up in memory before
you start writing an assembly language program. Essentially, it
creates a table with the ASCII values of your message. Be sure to
put the message somewhere out of the way of both your assembly
language program and BASIC. If you're working up in the 49152
area, put the message table up around 49200 or 49300 for short
programs or in 828 for longer programs if you're not using a
215
cassette. Note where you put your message, and then when you
write an assembly language program, you simply read that area of
memory for your message. The BASIC program automatically
ends the message with ASCII 35, the value for a pound sign (#); so
don't put any other pound signs in your message or prompt. A se-
cond BASIC program checks your message for you.
MESSAGE MAKER
10 PRINT CHR$(147)
20Y = 1
30 INPUT'STARTTABLE ";TS
40 INPUT'MESSAGE ";MS$
50FORX = TSTOTS + LEN(MS$)-1
60S$=MID$(MS$,Y,1)
70 P = ASC(S$)
80 POKE X,P : Y = Y + 1 : NEXT
90 POKE X,35
MESSAGE CHECKER
10 INPUT'START ADDR ";SA
20 P= PEEK(SA) : IF PEEK(SA) = 35 THEN END
30 PRINT CHR$(P); : SA = SA + 1 : GOTO 20
To test this system, use 49200 as the start address for your
message. (Write your name or something original like that.) Once
you've checked to see the message is in place with your MESSAGE
CHECKER program, enter the following and then execute it:
KIDS' ASSEMBLER - MESSAGE READ/PRINT
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
49157
LDA-X
49200
49160
CMP#
35
49162
BEQ
49171
49164
JSR
$E716
216
49167 I NX
49168 JMP 49157
49171 RTS
Now that you see the principle involved in making and storing
ASCII messages to be retrieved from an assembly language pro-
gram, we'll put together a BASIC program that will make it even
easier. Rather than having to write a new message or prompt from
our BASIC program every time we need one, why not write and
save little "message tables" we can use whenever we need them. In
that way, we can load the messages we're going to need into
memory, just as we would any assembly language program. By
keeping track of different start addresses for each message table and
making sure they do not overlap, we can have a whole dictionary of
useful messages and prompts. The following program will let you
enter a message, and then it will save it. Each message will end with
a "termination character", the pound sign (#), so that you can use
the CMP# compare-and-branch routine we examined. In that way,
you won't have to memorize the length of the message; only its star-
ting address.
MESSAGE MAKER/SAVER
10PRINTCHR$(147)
20Y=1
30 INPUT'START TABLE ";TS
40 INPUP'MESSAGE ";MS$
50 FOR X = TS TO TS + LEN(MS$) - 1
60S$ = MID$(MS$,Y,1)
70 P = ASC(S$)
80POKEX,P:Y = Y+1 : NEXT
90 POKE X,35
100SA = TS:MN$=MS$
110 MN$ = "0:" + MN$ + STR$(SA) + ",P,W"
120 LB = SA - INT(SA/256)*256
130HB=INT(SA/256)
140 OPEN2,8,2,MN$
150 PRINT#2,CHR$(LB) + CHR$(HB)
160P=PEEK(SA)
217
170 PRINT#2,CHR$(P)
180 IF P = 35 THEN 200
190SA = SA + 1 : GOTO 160
200 CLOSE2
210 END
SUMMARY
This chapter has shown you how to write assembly language pro-
grams that interact with the user. Using either the keyboard or
joystick, the programs respond to the user's actions. Then, in turn,
by outputting information to the user in the form of prompts and
messages, the user knows what to do next. We spent most of our
time with ASCII messages since they most clearly demonstrate
what's going on in your program and computer. However, as we
saw with changing the background colors, there's more we can do
with the keyboard and joystick. In the next chapter, we will see how
to manipulate all kinds of graphics with interactive programs.
218
CHAPTER 13
HOT GRAPHICS
Introduction
This chapter will be a lot of fun. From BASIC, you probably
have found that a good deal of work with graphics involves POKEs
and PEEKs. In other words, most of the really interesting graphics
requires machine language programming from BASIC. As a result
of having dealt with machine code and graphics already, you will
find what we're doing in this chapter pretty familiar. However, in-
stead of POKEing and PEEKing, we'll use our assembly language
instructions to do much the same thing and do it a lot faster. We'll
divide the chapter into two major sections:
1. Low resolution color and graphics
2. Animation
In the first section, we'll examine color control and using and
changing characters. Since we've already used a lot of examples
with background, border and character colors, color control with
assembly language will be familiar to you. We will see how by using
various characters, we can create low resolution graphics.
219
The second section will cover animation. In BASIC you may
have discovered that movement is often slow and jerky, but in
assembly language you will actually have to slow down animated
objects so that you can see them! We'll have a separate discussion
of sprite animation in the next chapter.
As an extra machine language related matter, we'll also see how
to save a screen to disk as a PRG file. This will allow you to LOAD
a screen directly from the disk without having to RUN or SYS The
program.
LOW RESOLUTION GRAPHICS
Low resolution graphics are created using the characters on your
keyboard. The pattern of those characters can be thought of as
blocks appearing on your screen. Perhaps the best way to see one of
these blocks is to print an inverse space to your screen.
POKE 55296,1 : POKE 1024,224
That stores the color white in location 55296 and the inverse
character for a SPACE in screen location 1024.
By lining up the various blocks, changing their color to what you
want, you can create low resolution figures. To get started, let's
draw a couple of parallel lines in different colors:
GENERAL
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore * =$C00©}
220
********************************************
LOW-RES LINES
**.*****************************************
{Commodore}
BAR1
= 55416
BAR2
= 55496
LIN1
= 1144
LIN2
= 1224
CLEAR
= $E544
{Merlin}
BAR1
EQU
55416
BAR2
EQU
55496
LIN1
EQU
1144
LIN2
EQU
1224
CLEAR
EQU
$E544
JSR
CLEAR
LDX
#$0
LDY
#2
START
LDA
#224
STA
LIN1.X
;1st line of
spaces
STA
LIN2,X
;2nd line of
spaces
TYA
STA
BAR1,X
;1st color of line
CLC
ADC
#5
;Change color
with ADC
STA
BAR2,X
;2nd color of
line
INX
CPX
#39
BNE
START
RTS
221
KIDS' ASSEMBLER
ADRS
OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
$0
49157
LDY#
2
49159
LDA#
224
49161
STA-X
1144
49164
STA-X
1224
49167
TYA
49168
STA-X
55416
49171
CLC
49172
ADC#
5
49174
STA-X
55496
49177
I NX
49178
CPX#
39
49180
BNE
49159
49182
RTS
Fat Lines!!
We used the X register to in-
dex our beginning address to get
a straight horizontal line. We
started our first line in screen
memory 1144 and our second
one in 1224. For color, we
started the corresponding color
addresses at 55416 and 55496.
By incrementing our X register
from to 39, we were able to
draw a line across the screen.
Also notice how we used the Y
register to get our first color. We never changed the value of Y, and
so whenever the program encountered the TYA instruction, it kept
putting a 2 (red color code) in the accumulator to be stored on the
color screen address. To get the second color (2 + 5=7, color
code for yellow), we used ADC. Of course, we could have simply
put LDA #7, but that wouldn't have been as weird.
222
To draw different kinds of horizontal lines, try substituting dif-
ferent characters for the space. Instead of 224, try 192, 195
226,239,246,248 and 249 to see what happens.
Since horizontal lines are so simple, vertical lines ought to be a
snap. Unfortunately, it doesn't work quite that way. With horizon-
tal lines, we could run one all the way across the screen using the X
register as an offset from the beginning address. However, with ver-
tical lines, our address jumps from the top of the screen to the bot-
tom are greater than 255. In fact, there is a difference of 40 between
each address we will need. Look down the left side of your screen
memory map. It looks like this:
1024
1064
1104
1144
1184
1224
1264
1304
1344
etc.
As you can see, the X register would poop out before it got halfway
down the screen. Therefore, we have to use more beginning offset
addresses. We'll use five base addresses for both our characters and
color. In that way we can stay well within our register limit of 255.
GENERAL - VERTICAL LINES
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $CO00}
{Commodore * = $CO00}
********************************************
* *
VERTICAL LINES
* *
********************************************
223
{Commodore}
BAR1
= 55316
BAR2
= 55556
BAR3
= 55796
BAR4
= 56036
BAR5
= 56276
LIN1
= 1044
LIN2
= 1284
LIN3
= 1324
LIN4
= 1564
LIN5
= 1804
CLEAR
= $E544
{Merlin}
BAR1
EQU
55316
BAR2
EQU
55556
BAR3
EQU
55796
BAR4
EQU
56036
BAR5
EQU
56276
LIN1
EQU
1044
LIN2
EQU
1284
LIN3
EQU
1324
LIN4
EQU
1564
LIN5
EQU
1804
CLEAR
EQU
$E544
JSR
CLEAR
LDX
#$0
START
LDA
#224
STA
STA
STA
STA
STA
LDA
LIN1,X
LIN2.X
LIN3,X
LIN4,X
LIN5,X
#2
STA
STA
BAR1.X
BAR2,X
STA
STA
BAR3.X
BAR4.X
224
INXR
STA
BAR5,>
LDY
#0
INX
INY
CPY
#40
BNE
INXR
CPX
#240
BNE
START
RTS
KIDS' ASSEMBLER - VERTICAL LINES
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
$0
49157
LDA#
224
49159
STA-X
1044
49162
STA-X
1284
49165
STA-X
1324
49168
STA-X
1564
49171
STA-X
1804
49174
LDA#
2
49176
STA-X
55316
49179
STA-X
55556
49182
STA-X
55796
49185
STA-X
56036
49188
STA-X
56276
49191
LDY#
49193
INX
49194
INY
49195
CPY#
40
49197
BNE
49193
49199
CPX#
240
49201
BNE
49157
49203
RTS
Another way to work with low resolution graphics is with your
CHROUT routine. Instead of having to deal with both color and
character screens, you can just deal with the characters. The screen
225
addresses make it handy to place blocks where you want them, but
with CHROUT, you can use the PLOT subroutine to place your
graphics. To see how this works, let's make a diagonal line.
In using the PLOT subroutine, you have to be sure to LDA your
character to be printed on the screen AFTER you PLOT. This is
because the accumulator can be scrambled by the PLOT subrou-
tine. Also, be sure to CLC before you JSR to PLOT ($FFF0),
otherwise you may end up reading and not setting the plot location.
(To read the plot location, you set the carry flag with SEC.)
GENERAL - DIAGONAL LINES
LABEL
OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore * = !
******************************** **********,
DIAGONAL PLOT
********************************************
{Commodore}
CHROUT
= $FFD2
CLEAR
= $E544
PLOT
= $FFF0
{Merlin}
CHROUT
EQU
$FFD2
CLEAR
EQU
$E544
PLOT
EQU
$FFF0
JSR
CLEAR
LDX
#0
LDY
#0
LDA
#18
;Character for
inverse.
JSR
CHROUT
START
CLC
226
JSR
PLOT
LDA
#32
;Character for
space.
JSR
CHROUT
INX
;Next row
INY
;Next column
CPX
#25
;ls it at the
bottom row
yet?
BNE
START
;lf not go back
and do it again
RTS
KIDS' ASSEMBLER - DIAGONAL PLOT
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
49157
LDY#
49159
LDA#
18
49161
JSR
$FFD2
49164
CLC
49165
JSR
$FFF0
49168
LDA#
32
49170
JSR
$FFD2
49173
INX
49174
INY
49175
CPX#
25
49177
BNE
49164
49179
RTS
Admittedly, low resolution graphics aren't much for detailed
drawings, but they're a lot of fun. Change the background and
border colors by inserting values between and 15 into $D021 and
$D020 . (These are the routines we used in some of our previous ex-
amples. The hexadecimal numbers are easier to remember than the
decimal ones once you get used to them.)
227
One way to have finer resolution in low resolution graphics is to
use characters that have the kinds of lines you want in your graphic
display. For example, if you want to have a nice thin diagonal line in
the DIAGONAL PLOT program above, just remove the lines that
inverse the screen (LDA #1 8 and JSR CHROUT), and use the value
109 instead of 32 to output to the screen. ASCII 109 is a left-leaning
angle. Put them together in a diagonal plot, and you will have a very
straight line instead of the "staircase" we got.
SAVING PLOTTED GRAPHICS
When you draw low-resolution figures with the PLOT
subroutine, you can save them as PRG files and load them directly
to your screen. Actually, it takes a lot less disk space to save your
graphics as machine language files and then load and SYS them,
but there are some applications where you might want to "overlay"
one set of graphics with another. Besides, it's interesting and useful
to know. (You can save anything on the screen, actually, and so this
method can be used to save text as well.)
Essentially, what you do is to scan the entire screen, and then use
the screen addresses (1024-2023) as your machine code file. Then
when you LOAD "GRAPHICFILE",8,1 your graphic will be on
the screen. Because your load address is the screen, it appears im-
mediately without the necessity of having to SYS it.
The following BASIC program is to be used in conjunction with
a machine language program you have put in memory. For exam-
ple, to save the DIAGONAL PLOT figure (not program) as a PRG
file, you first load your program into memory like you would nor-
mally do. However, instead of SYSing the program at this time,
enter NEW and RETURN. Then LOAD and RUN the following
program. It will first SYS the program in memory so that the only
thing on your screen is the graphic figures. Then, it will treat the en-
tire screen as a machine language program and put it on disk just as
any other machine language program would be. They take up about
four blocks of disk space compared to one block taken up by small
assembly language programs such as DIAGONAL PLOT. Here's a
summary of the steps to use:
228
Step 1 . Either write a machine language graphic program in your
assembler or load one from disk.
Step 2. Enter NEW and RETURN.
Step 3. LOAD the SAVE PLOT program and RUN it.
SAVE PLOT
10 INPUT "NAME OF FILE TO SAVE"; NF$
20 NF$ = "0:" + NF$ + ",P,W"
30 INPUT "START ADDRESS";TER
40 SYSTER
50BA = 1024:EA = 2023
60 LB = BA-INT(BA/256)*256
70HB=INT(BA/256)
80 OPEN2,8,2,NF$
90 PRINT#2,CHR$(LB) + CHR$(HB)
100FORX=BATOEA
110P=PEEK(X)
120 PRINT#2,CHR$(P)
130 NEXT
140 CLOSE2
Take a look at Line 40. You probably know that there's no such
BASIC command as SYSTER (pronounced 'sister'), but there it is,
and everything works fine. It's just a way to do weird things with
your computer. Since the starting address of your machine program
in memory is INPUT in the variable TER, what it actually does is to
SYS the variable TER. Put them together, and there you have it. (If
you think that's dumb, use the variable name BOOMBAH and see
what happens.)
Before we go on to the next section, you're probably wondering
why we didn't use our program to save program files of graphics
created with storage to the screen addresses instead of just ones
created with the PLOT and CHROUT subroutines. Since most
Commodore 64's require that a corresponding color be included
when you STA an ASCII value in a screen address, we would have
229
had to have two files saved; one for the screen and one for the color.
You can do it if you want, but it didn't seem to be worth the bother.
ANIMATION
Animation in assembly language requires some programming
concentration, but it's so spectacular when you're finished, it's
worth it. All animation is based on the illusion of drawing a figure
in one place, erasing it, and drawing it in another place. It works
just like cartoons in the movies. A figure is drawn, shown for an in-
stant on the screen, and then it is removed (erased) and another
figure is put in its place.
Your computer makes animation very simple since it can create
and erase figures in different places on the screen at high speeds. In
fact, with assembly/machine language, it is often too fast. To see
the difference in speed between a BASIC and assembly language
program doing the same thing, look at try the following programs.
BASIC ANIMATION
10 PRINT CHR$(147)
20 FOR X = 1 TO 10 : PRINT : NEXT
30 FOR X = 1 TO 39 : PRINT CHR$(113); : PRINT
CHR$(157); CHR$(32);
40 NEXT
50PRINTCHR$(113)
The ball really sails along in BASIC. A little flicker maybe, but it
moves nicely. Try it in assembly language now.
♦NOTE: We used different beginning addresses for Merlin
and the Commodore assemblers. The default ORG for Merlin
is $8000, and if we use it, we can test assembled programs in
the monitor before we save the program to disk. From the Edit
Mode, enter MON {RETURN} and you will be in the
monitor, indicated by a '$' prompt. To test the program, just
enter 8000g. All values in the Merlin monitor are assumed to
be hexadecimal. We have avoided the $8000 address for the
beginning of your object code because you might be using a
230
plug-in ROM. However, as we get into more complex and
longer programs, you'd better start using the Merlin monitor
for debugging your programs. If you have a ROM installed,
take it out before you start your assembly language program-
ming with Merlin. If your program ORG is at $C000, it may
interfere with Merlin or the monitor.
GENERAL
LABEL
OPCODE OPERAND COMMENT
{Merlin
{Commodore
ORG $8000}
* = $C000
********************************************
ANIMATION 1
* *
********************************************
{Commodore}
CLEAR
CHROUT
PLOT
EX
WHY
BALL
SPACE
= $E544
= $FFD2
= $FFF0
= $C200
= $C202
= $C204
= $C206
{Merlin}
CLEAR
CHROUT
PLOT
EX
WHY
BALL
SPACE
EQU
EQU
EQU
EQU
EQU
EQU
EQU
$E544
$FFD2
$FFF0
$8200
$8202
$8204
$8206
231
START
JSR
CLEAR
LDX
#10
;Set to row 10.
STX
EX
LDY
#0
;Set to column
0.
STY
WHY
LDA
#113
;ASCII value for
ball
STA
BALL
LDA
#32
;ASCII value for
space
STA
SPACE
LDX
EX
LDY
WHY
CLC
JSR
PLOT
;Plot the ball
LDA
BALL
;Load the ball
JSR
CHROUT
; Print the ball
LDX
EX
;Load X register
with last row
plot
LDY
WHY
;Load Y register
with last
column plot
CLC
JSR
PLOT
;Plot the space
LDA
SPACE
;Load the space
JSR
CHROUT
; Erase the ball
with the space
INC
WHY
; Increment the
column value
CO
LDY
WHY
;Load the Y
register with
the next
column
CPY
#38
;ls it near the
last column
BNE
START
;lf not print and
232
erase another
ball
CLC
JSR
PLOT
LDA
BALL
JSR
CHROUT
;Put a ball on
the screen so
there's
something left
RTS
KIDS' ASSEMBLER - ANIMATION 1
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
10
49157
STX
$C200
49160
LDY#
49162
STY
$C202
49165
LDA#
113
49167
STA
$C204
49170
LDA#
32
49172
STA
$C206
49175
LDX
$C200
49178
LDY
$C202
49181
CLC
49182
JSR
$FFF0
49185
LDA
$C204
49188
JSR
$FFD2
49191
LDX
$C200
49194
LDY
$C202
49197
CLC
49198
JSR
$FFF0
49201
LDA
$C206
49204
JSR
$FFD2
49207
INC
$C202
49210
LDY
$C202
49213
CPY#
38
49215
BNE
49175
233
49217
CLC
49218
JSR
$FFF0
49221
LDA
$C204
49224
JSR
$FFD2
49227
RTS
You may have thought you did something wrong. If all you saw
was the ball on the right side of the screen after you SYSed the pro-
gram, you keyed it in correctly. Animation is so fast in
assembly/machine language that you can't see the movement unless
you slow it down. To do that, we'll put in a PAUSE loop. In fact,
we'll have to put in a nested PAUSE loop since even a loop of 255
won't slow the movement down enough. All the pause loop does is
to run through an "empty loop" to slow down a program. In this
case, we ran through the loop 2550 times. Edit ANIMATION 1 so
that it includes the PAUSE loop in ANIMATION 2, and run the
program again. Now you can see the ball move smoothly across the
screen. To increase or decrease the speed of the ball, increase or
decrease the CPY #$0A.
GENERAL - ANIMATION 2
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $8000}
{Commodore * = $C00O}
********************************************
* *
ANIMATION 2
* *
********************************************
{Commodore}
CLEAR =$E544
CHROUT =$FFD2
PLOT =$FFF0
EX =$C200
WHY =$C202
234
BALL
= $C204
SPACE
= $C206
{Merlin}
CLEAR
EQU
$E544
CHROUT
EQU
$FFD2
PLOT
EQU
$FFF0
EX
EQU
$8200
WHY
EQU
$8202
BALL
EQU
$8204
SPACE
EQU
$8206
JSR
CLEAR
LDX
#10
STX
EX
LDY
#0
STY
WHY
LDA
#113
STA
BALL
LDA
#32
STA
SPACE
START
LDX
EX
LDY
WHY
CLC
JSR
PLOT
LDA
BALL
JSR
CHROUT
LDY
#0 ;Begin pause
loop.
PAUSE1
LDX
#0
PAUSE2
INX
CPX
#$FE
BNE
PAUSE2
INY
CPY
#$0A
BNE
PAUSE1 ;End pause
loop.
LDX
EX
LDY
WHY
CLC
JSR
PLOT
235
LDA
SPACE
JSR
CHROUT
INC
WHY
LDY
WHY
CPY
#38
BNE
START
CLC
JSR
PLOT
LDA
BALL
JSR
CHROUT
RTS
KIDS' ASSEMBLER - ANIMATION 2
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDX#
10
49157
STX
$C200
49160
LDY#
49162
STY
$C202
49165
LDA#
113
49167
STA
$C204
49170
LDA#
32
49172
STA
$C206
49175
LDX
$C200
49178
LDY
$C202
49181
CLC
49182
JSR
$FFF0
49185
LDA
$C204
49188
JSR
$FFD2
49191
LDY#
49193
LDX#
49195
I NX
49196
CPX#
$FE
49198
BNE
49195
49200
INY
49201
CPY#
$0A
49203
BNE
49193
49205
LDX
$C200
236
49208
LDY
$C202
49211
CLC
49212
JSR
$FFF0
49215
LDA
$C206
49218
JSR
$FFD2
49221
INC
$C202
49224
LDY
$C202
49227
CPY#
38
49229
BNE
49175
49231
CLC
49232
JSR
$FFF0
49235
LDA
$C204
49238
JSR
$FFD2
49241
RTS
That was a lot of work to get that crummy ball moving across the
screen, and if you used the Kids' Assembler, you might be thinking
the French Foreign Legion would be involve less pain. You might
even be desperate enough to go back to BASIC. (God forbid!) On
the other hand, if you used an assembler with a decent editor, all
you had to do was to insert the eight lines for the PAUSE loop. If
you have a birthday coming up, it's near Christmas or you have a
half of ton of aluminum cans to take to the recycling center, think
about getting a good assembler.
EXTERNAL CONTROL OF MOVEMENT
Now that we have seen how to move objects, let's see how to con-
trol them with an external device. We'll use the joystick for our ex-
amples, but you could do the same thing with the keyboard. Just
substitute the SCNKEY and GETIN routines for the joystick ones
in the following programs.
The nice thing about using the PLOT subroutine in animation is
that you can use the X and Y registers to place things on the screen
in sequential locations. However, when you start moving all over
the screen, PLOT can really scramble things, especially your brain.
Therefore, we will begin using the ASCII code for your cursor con-
trol.
237
CURSOR CONTROL CODES
Up
145
$91
Down
17
$11
Left
157
$9D
Right
29
$1D
We'll use the CHROUT subroutine for moving both the cursor
and the character. The trick is in remembering that CHROUT
moves everything to the RIGHT. Therefore, when we move down,
we actually move down and right with CHROUT. Therefore, we
will have to make adjustments when we move in any other direction
than right. To get started, we'll make a blinking cursor and move it
around the screen. Again, note the different addresses used by
Merlin and Commodore assemblers for the starting location.
GENERAL - JOYSTICK CURSOR
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $8000}
{Commodore *- !
********************************************
JOYSTICK CURSOR
********************************************
{Commodore}
CLEAR
JSTICK
OFSET
FIRE
INVERSE
NORMAL
MARK
CHROUT
$E544 __..
:$DC01 J^g>
;$C200
$C202
$C204
:$C206 §
:$C208 ^
:$FFD2
■JOYSTICK
238
{Merlin}
CLEAR
EQU
$E544
JSTICK
EQU
$DC01
BB8ET
$8200
EORIE
$8202
INVERSE
EQU
$8204
NORMAL
EQU
$8206
MARK
EQU
$8208
CHROUT
EQU
$FFD2
JSR
CLEAR
LDA
#$FF
OR value
STA
OFSET
LDA
#16
;Fire button
STA
FIRE
LDA
#32
;Space for the
cursor
STA
MARK
LDA
#18
;lnverse Code
STA
INVERSE
LDA
#146
; Normal code
STA
NORMAL
START
LDA
JSTICK
EOR
OFSET
CMP
#1
;Joystlck up?
BEQ
UP
CMP
#2
;Joystick
down?
BEQ
DOWN
CMP
#4
;Joystick left?
BEQ
LEFT
CMP
#8
;Joystlck right?
BEQ
RIGHT
CMP
FIRE
;Fire button
pressed?
BEQ
END
;lf so then end.
CURSOR
LDA
MARK
;Load the
space
JSR
CHROUT
;Print the space
LDA
#157
;Load the left
cursor
239
JSR
CHROUT
;Back up
LDA
NORMAL
JSR
CHROUT
;Set normal
LDA
MARK
JSR
CHROUT
LDA
#157
;Back up
JSR
CHROUT
LDA
INVERSE
JSR
CHROUT
;Set inverse
JMP
START
;Go do It again
UP
LDA
#145
JMP
PRINT
;Print up cursor
DOWN
LDA
#17
JMP
PRINT
; Print down
cursor
LEFT
LDA
#157
JMP
PRINT
;Print left cursor
RIGHT
LDA
#29
PRINT
JSR
CHROUT
JMP
CURSOR
END
RTS
{Commodore
.END}
KIDS' ASSEMBLER • JOYSTICK CURSOR
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDA#
$FF
49157
STA
$C200
49160
LDA#
16
49162
STA
$C202
49165
LDA#
32
49167
STA
$C208
49170
LDA#
18
49172
STA
$C204
49175
LDA#
146
49177
STA
$C206
49180
LDA
$DC01
49183
EOR
$C200
240
49186
CMP#
1
49188
BEQ
49244
49190
CMP#
2
49192
BEQ
49249
49194
CMP#
4
49196
BEQ
49254
49198
CMP#
8
49200
BEQ
49259
49202
CMP
$C202
49205
BEQ
49267
49207
LDA
$C208
49210
JSR
$FFD2
49213
LDA#
157
49215
JSR
$FFD2
49218
LDA
$C206
49221
JSR
$FFD2
49224
LDA
$C208
49227
JSR
$FFD2
49230
LDA#
157
49232
JSR
$FFD2
49235
LDA
$0204
49238
JSR
$FFD2
49241
JMP
49180
49244
LDA#
145
49246
JMP
49261
49249
LDA#
17
49251
JMP
49261
49254
LDA#
157
49256
JMP
49261
49259
LDA#
29
49261
JSR
$FFD2
49264
JMP
49207
49267
RTS
When you assemble and run this program, you will see a flashing
cursor made by the SPACE character being turned normal and in-
verse rapidly. When you move your joystick, the cursor will be
wholly out of control, jumping clear across the screen with a quick
movement of your joystick. Just as we saw with animation,
241
machine language is just too blasted fast. We'll have to slow it down
with a delay loop to get single space control over it.
Now that we have seen some general movement principles with
our cursor, let's draw with the joystick. This is not "animated"
movement in that what we draw is not erased and then re-drawn.
However, very similar principles apply when controlling what hap-
pens with graphics on the screen. We'll also add a couple of new
tricks.
First, we saw in the last program that we had to keep drawing and
backing up. This required several LDA's and JSR's throughout the
program. Since we know we're going to have to back up, why not
make a subroutine that will do that. The subroutine will be accessed
just like the built-in subroutines using JSR. However, since we're
writing the subroutine as part of our own program, we have to in-
clude an RTS to get back to our jump-off point. It works just like
GOSUB and RETURN in BASIC.
Second, we're going to put a pause loop in our "joystick scan" to
slow things down a bit. In the last program, when you moved the
joystick, the cursor hopped all the way across the screen. This was
because the scan was so quick that it was able to read the direction
of the joystick and jump to the subroutines several times before you
could put it in neutral. (In fact, you may want to increase the pause
loop we put in this program!)
The program draws low resolution lines on the screen. To keep it
simple and a little more flexible, we'll use the joystick LEFT to serve
as an eraser. Therefore, if you move the joystick UP, DOWN or
RIGHT, a line will be drawn. Move it left, and anything the cursor
hits will be erased.
GENERAL
LABEL OPCODE OPERAND COMMENT
{Merlin ORG
{Commodore *=$C000}
242
. ************
. *
. *
. *
******************
JOYSTICK DRAW
. ******************************
{Commodore}
CLEAR
= $E544
JSTICK
= $DC01
OFSET
= $C20
FIRE
= $C202
INVERSE
= $C204
NORMAL
= $C206
MARK
= $C208
CHROUT
=$FFD2
{Merlin}
CLEAR
EQU
$E544
JSTICK
EQU
$DC01
OFSET
EQU
$8200
FIRE
EQU
$8202
INVERSE
EQU
$8204
NORMAL
EQU
$8206
MARK
EQU
$8208
CHROUT
EQU
$FFD2
JSR
CLEAR
LDA
#$FF
STA
OFSET
LDA
#16
STA
FIRE
LDA
#32
STA
MARK
LDA
#18
STA
INVERSE
LDA
#146
STA
NORMAL
START
LDA
JSTICK
EOR
OFSET
CMP
#1
BEQ
UP
243
LDA
INVERSE
JSR
CHROUT
LDX
#0
; Beg in pause
loop
PAUSE
INX
CPX
#254
BNE
PAUSE
;End pause loop
JMP
START
UP
JSR
BACK
LDA
#145
JMP
PRINT
DOWN
JSR
BACK
LDA
#17
JMP
PRINT
LEFT
LDA
#157
JSR
CHROUT
JMP
PRINT
PRINT
JSR
CHROUT
LDA
MARK
JSR
CHROUT
JMP
CURSOR
BACK
LDA
#157
;Subroutine
JSR
CHROUT
RTS
END
RTS
{Commodore
.END}
KIDS' ASSEMBLER - JOYSTICK DRAW
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDA#
$FF
49157
STA
$C200
49160
LDA#
16
49162
STA
$C0202
49165
LDA#
32
49167
STA
$C208
49170
LDA#
18
49172
STA
$C204
244
49175
LDA#
146
49177
STA
$C206
49180
LDA
$DC01
49183
EOR
$C200
49186
CMP#
1
49188
BEQ
49247
49190
CMP#
2
49192
BEQ
49255
49194
CMP#
4
49196
BEQ
49263
49198
CMP#
8
49200
BEQ
49271
49202
CMP
$00202
49205
BEQ
49289
49207
LDA
$C208
49210
JSR
$FFD2
49213
JSR
49285
49216
LDA
$C206
49219
JSR
$FFD2
49222
LDA
$0208
49225
JSR
$FFD2
49228
JSR
49285
49231
LDA
$0204
49234
JSR
$FFD2
49237
LDX#
49239
INX
49240
CPX#
254
49242
BNE
49239
49244
JMP
49180
49247
JSR
49285
49250
LDA#
145
49252
JMP
49271
49255
JSR
49285
49258
LDA#
17
49260
JMP
49271
49263
LDA#
157
49265
JSR
$FFD2
49268
JMP
49271
49271
JSR
$FFD2
49274
LDA
$0208
245
49277
JSR
$FFD2
49280
JMP
49207
49283
LDA#
157
49285
JSR
$FFD2
49288
RTS
49289
RTS
That was a long one! Be sure to note the two RTS instructions at
the end of the program. The first one is to return to the program
position that initiated the JSR. It works like RETURN. The second
one is to return to BASIC and end the machine level program. Of
course the RTS that returns to BASIC does not have to be at the end
of the program, but it must be the last RTS encountered in the pro-
gram flow.
SUMMARY
In this chapter we saw how to work with low resolution graphics
and animation. However, we learned a few new tricks in assembly
language programming, as well. We wrote our own subroutine that
we JSRed and RTSed from and saw how to make delay loops. By
combining previous skills, we even made a drawing program with
the joystick.
We did some work with color, but not a great deal. This was
because we already know how to change the colors to anything we
want from examples in previous chapters. Also, since we were do-
ing a good deal of work with new concepts, more color may have
been confusing. Besides, I wanted you to test some of your own
skills in this area. Experiment with different border, background
and character colors with the programs. You'll be surprised by the
difference it makes.
In the next chapter, we will examine sprite graphics and sound.
There, we will combine new tricks with some of the those we learn-
ed in this chapter. We will be learning the fundamentals of arcade
game assembly language programming.
246
CHAPTER 14
BLAZING SPRITES
AND MONSTROUS SOUNDS
SPRUE GRAPHICS
If you've worked with sprites in BASIC, you will find working
with them in assembly language is easier! Since most of the set-up
you had in BASIC involved POKEs and PEEKs, you were actually
working with machine language. We all know that assembly
language is simpler than machine language; so this ought to be a
snap for you BASIC sprite programmers. For those of you who
have not worked with sprites, we'll take it a step at a time.
In case you don't know what a sprite is on the Commodore 64, let
me explain. Basically, it is a 63 byte graphic image. Each byte is
given a value to turn on a configuration of pixels; little dots on the
screen. From our discussion of binary math and how the various
registers look, let's take a look at a single byte.
7 6 5 4 3 2 10
10 11111
On Off Off On On On On On
247
On your screen, the above configuration would look something like
the following if it were magnified:
Each sprite is composed of three columns and 21 rows. Each row
is made up of three bytes. Since each byte has 8 bits that can be turn-
ed on or off, it is clearer to think of a sprite graphic as a 21 by 24
matrix of bits or pixels.
Sprite Matrix
Column 1
Column 2 Column 3
Row
xxxxxxxx
xxxxxxxx
xxxxxxxx
1
xxxxxxxx
xxxxxxxx
xxxxxxxx
2
xxxxxxxx
xxxxxxxx
xxxxxxxx
3
xxxxxxxx
xxxxxxxx
xxxxxxxx
4
xxxxxxxx
xxxxxxxx
xxxxxxxx
5
xxxxxxxx
xxxxxxxx
xxxxxxxx
6
xxxxxxxx
xxxxxxxx
xxxxxxxx
7
xxxxxxxx
xxxxxxxx
xxxxxxxx
8
xxxxxxxx
xxxxxxxx
xxxxxxxx
9
xxxxxxxx
xxxxxxxx
xxxxxxxx
10
xxxxxxxx
xxxxxxxx
xxxxxxxx
11
xxxxxxxx
xxxxxxxx
xxxxxxxx
12
xxxxxxxx
xxxxxxxx
xxxxxxxx
13
xxxxxxxx
xxxxxxxx
xxxxxxxx
14
xxxxxxxx
xxxxxxxx
xxxxxxxx
15
xxxxxxxx
xxxxxxxx
xxxxxxxx
16
xxxxxxxx
xxxxxxxx
xxxxxxxx
17
xxxxxxxx
xxxxxxxx
xxxxxxxx
18
xxxxxxxx
xxxxxxxx
xxxxxxxx
19
xxxxxxxx
xxxxxxxx
xxxxxxxx
20
xxxxxxxx
xxxxxxxx
xxxxxxxx
21
In the above matrix, suppose that the 'x' marks represent a 0. If
we turned on a pixel, it would be a ' + \ To draw a character, we
just have to put little ' + ' marks in the shape we want. To keep it
simple, we'll draw a cross:
248
Column 1 Column 2 Column 3
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
++++++++
++++++++
++++++++
++++++++
++++++++
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + +
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
++++++++
++++++++
++++++++
++++++++
++++++++
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
xxxxxxxx
Row
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Next, to envision our cross as a set of l's and zero's, let's change
the X's to 's and the + 's to l's.
Column 1 Column 2 Column 3
11
11
11
11
11
11
11
11
11
11
1111
1111
1111
1111
1111
1111
1111
1111
1111
1111
oooooooo
Row
1
2
3
4
5
6
7
8
9
10
249
00000000
11111111
00000000
11
oooooooo
11111111
00000000
12
00000000
11111111
00000000
13
00000000
11111111
00000000
14
00000000
11111111
00000000
15
00000000
11111111
00000000
16
00000000
11111111
00000000
17
oooooooo
11111111
oooooooo
18
00000000
11111111
oooooooo
19
oooooooo
11111111
00000000
20
00000000
11111111
00000000
21
To get our cross into the computer, we will have to change the
binary values into decimal or hex so that we can put them into
memory. (We could do it with binary numbers, but that would be
impossible with the Kids' Assembler.) We know a byte with
00000000 is equal to and a byte with 1 1 1 1 1 1 1 1 is 255 or $FF. To
arrange our cross in the correct sequential order, we move from left
to right and top to bottom. In the column to the right of our figure,
we'll place the decimal values.
Column 1
Column 2 Column 3
Value
00000000
11111111
00000000
0,255,0
oooooooo
11111111
00000000
0,255,0
oooooooo
11111111
00000000
0,255,0
00000000
11111111
00000000
0,255,0
00000000
11111111
oooooooo
0,255,0
11111111
11111111
11111111 255,255,255
11111111
11111111
11111111 255,255,255
11111111
11111111
11111111 255,255,255
11111111
11111111
11111111 255,255,255
11111111
11111111
11111111 255,255,255
00000000
11111111
00000000
0,255,0
oooooooo
11111111
oooooooo
0,255,0
oooooooo
11111111
00000000
0,255,0
oooooooo
11111111
oooooooo
0,255,0
00000000
11111111
00000000
0,255,0
00000000
11111111
00000000
0,255,0
00000000
11111111
oooooooo
0,255,0
oooooooo
11111111
oooooooo
0,255,0
250
00000000 11111111 00000000 0,255,0
00000000 11111111 00000000 0,255,0
00000000 11111111 00000000 0,255,0
To place a sprite into memory, you begin with Row 1 Column 1
and move to Row 1 Column 2, Row 1 Column 3 , Row 2 Column 1 ,
Row 2 Column 2 etc. and place the values in sequential memory
locations. (You snake along in other words.) Thus, in the first two
rows, we would place the values into memory in the following
order:
Location Value Column Row
#1
1
1
#2
255
2
1
#3
3
1
#4
1
2
#5
255
2
2
#6
3
2
Of course, you're not limited to values of or 255. Depending on
the bit configuration, your sprite bytes can be any combination of
values between and 255. For example, you might have a con-
figuration that looks like the following:
00001111 00111100 1111000 15,60,240
00000111 00011000 1110000 7,24,224
00000011 01100110 1100000 3,102,192
Use the BINARY - DECIMAL conversion program in Chapter 5 to
easily make the conversion from binary to decimal.
Further on in this chapter we will write a "Sprite Assembler" in
BASIC that will store your sprites and save them. Before that,
though, we must first go over the procedure for getting sprites to
work.
SPRITE CREATION
The only way to work with sprites and assembly language is to
GET ORGANIZED! Programming sprites is actually quite simple
251
once you have everything set up in a simple sequence and know
what registers to use. To begin, let's outline the basic sequence of
programming sprites
SIMPLE SPRITE SEQUENCE
1. Store block number into POINTER.
2. ENABLE Sprite
3. Store sprite COLOR in sprite color register
4. BUILD sprite and store it.
5. Set horizontal HIGH bit to 1 or©.
6. MOVE sprite
The above sequence provides the order of events in your pro-
gram. Assembly language uses the same sequence as BASIC; so
there's nothing new about it. Now, let's look at each step closely.
1. Store Block Number into POINTER.
We will be storing our sprites in available memory blocks of 63
bytes each. Therefore, we must find some 63 byte blocks we can
use.
Block Number Addresses:
11 704-767 $2C0-$2FF
13 832-895 $340-$37F
14 869-959 $380-$3BF
15 960-1023 $3C0-$3FF
The block number refers to the number of 64 byte blocks (not 63
since 64 is evenly divided into 256) below the starting address. For
example, Block is at addresses 0-63, Block 1 at 64-127 and so
forth. Without rearranging memory, those four blocks are about as
much as we can handle. Therefore, we can build four sprites. (With
memory management, you can get up to eight sprites going.)
Now that we know what a block number is, we must store that
value in a register that points to the block in which we will store our
252
sprite. The pointer address begins at 2040 ($7F8). To get the correct
pointer for our sprite, we add the sprite number to the base address.
Sprite number POINTER Register
Sprite
2040
$FF8
Sprite 1
2041
$FF9
Sprite 2
2042
$FFA
Sprite 3
2043
$FFB
Sprite 4
2044
$FFC
Sprite 5
2045
$FFD
Sprite 6
2046
$FFE
Sprite 7
2047
$FFF
For example, let's say we
wanted to use block 13 (loca-
tions 832-895) and Sprite 1. We
would do the following:
LDA
STA
#13
2041
That would do it. If you've
worked with sprites in BASIC,
it's just like POKE 2041,13.
2. ENABLE Sprite
The register at $D015 (53269)
is the ENABLE register for
sprites. To enable a specific
sprite, you load $D015 with the
sprite value, not the sprite
number.
Sprite Pointer
Sprite Number Sprite Value
Sprite© 1
Sprite 1 2
Sprite2 4
253
Sprite 3 8
Sprite4 16
Sprites 32
Sprite6 64
Sprite7 128
To enable Sprite 2, you would do the following:
LDA #4
STA $D015
REMEMBER, the value 4 is for Sprite 2, not Sprite 4.
3. Store Sprite COLOR in Sprite Color Register.
Each sprite has a color value from 0-15 ($0-$F). The color values
correspond to the standard colors we've used so far for
background, border and character colors. Each sprite has its own
color register beginning at $D027 (53287.) To determine which col-
or register to use, just add the sprite number to the base address of
$D0 27 (53287).
Sprite Number Color Register
Sprite
$D027/53287
Sprite 1
$D028/53288
Sprite 2
$D029/53289
Sprite 3
$D02A/53290
Sprite 4
$D02B/53291
Sprite 5
$D02C/53292
Sprite 6
$D02D/53293
Sprite 7
$D02E/53294
For example, to store the color RED in Sprite 1 color register, simp-
ly use the following:
LDA #2 ;Value for color RED
STA $D028
254
The process works just like storing the background color in $D021
except it turns the sprite color on instead of the background color.
4. BUILD Sprite
At this stage, you load the sprite values (63 of them) into the
block you stored in the pointer register in Step 1, For example, if
you used Block 13, you would store the sprite values in locations
832-895 ($340-$37F). We will discuss the several ways sprites can be
built and stored further on in this chapter.
5. Set horizontal HIGH bit to or 1.
The register at $D0 10 holds the high byte for all sprite horizontal
locations. If it is set to 1 then all horizontal (X) values are 256 or
higher. If it is set to 0, all values are from 0-255. The horizontal
screen for sprites is 320 dots wide. For the first 255 dots, the high
byte is set to 0, and for 256 to 320, it must be set to 1 . For the time
being, we'll be setting it to 0.
LDA #0
STA $D010
Later we will discuss full horizontal movement.
6. MOVE Sprite
To move your sprite, the X (horizontal) and Y (vertical) positions
are set in register pairs beginning at $D000-$D000 1 (53248-53249).
The first of the pair is the X position and the second is the Y posi-
tion.
Register Pair
Sprite Number X Y
Sprite $D000/53248 $D001/53249
Sprite 1 $D002/53250 $D003/53251
Sprite 2 $D004/53252 $D005/53253
Sprite 3 $D006/53254 $D007/53255
Sprite 4 $D008/53256 $D009/53257
255
Sprite 5 $D00A/53258 $D00B/53259
Sprite 6 $D00C/53260 $D00D/53261
Sprite 7 $D00E/53262 $D00F/53263
To move a sprite, the X and Y values of the corresponding
registers are changed. The good thing about sprites is that you do
not have to erase a sprite to move it. You simply change the X and Y
values of the corresponding registers. For example, to move Sprite 1
across the screen horizontally at vertical location 100, you would
use the following:
LDX #0
LDY #100
STY $D003
MOVE STX $D002
INX
CPX #255
BNE MOVE
Of course, you can also change the value of the Y register to move
vertically, or change both the X and Y values to move diagonally.
At this point we have all of the basic information for making a
sprite. For our first example, we will simply make a big block by fill-
ing in our sprite area with 255 ($FF). We will then move our block
across the screen. Since machine language is so fast, we will have to
slow our sprite down with a pause loop. We will use Sprite for our
first example since it uses the base addresses of all the registers.
Also, we will use hexadecimal values for the registers since they are
easier to remember. Most of them start with $D0, and you can
think of them as "do" something or other.
GENERAL - SPRITE ZERO STARTER
LABEL OPCODE OPERAND COMMENT
{Merlin ORG
{Commodore * = $C000}
256
. *
**********
**********
i*************
*
. *
SPRITE ZERO STARTER
*
. ********************************************
{Commodore}
SPRITE©
= $7F8
ENABLE
= $D015
COLOR©
= $D©27
SPOX
= $D000
SPOY
= $D©01
MSBX
= $D01©
SHOUSE
= $034©
{Merlin}
SPRITE©
EQU
$7F8
ENABLE
EQU
$D015
COLOR©
EQU
$D027
SPOX
EQU
$D000
SP©Y
EQU
$D001
MSBX
EQU
$D01©
SHOUSE
EQU
$034©
JSR
$E544
LDA
#$0D
; Block 13 or
$0D
STA
SPRITE©
;Store it in
pointer for
Sprite ©
LDA
#1
;Sprite © enable
value
STA
ENABLE
;Store it in
enable register
LDA
#2
;Color red
STA
COLOR©
;Color register
for Sprite ©
LDX
#©
LDA
#0
CLEANUP
STA
SHOUSE.X
257
BUILD
MOVE
PAUSE
;Store zeros in
INX
sprite area
CPX
#63
BNE
CLEANUP
LDX
#0
LDA
#$FF
STA
SHOUSE.X
;Fill up sprite
area with $FF
or 255
INX
;to make solid
block
CPX
#63
BNE
BUILD
LDA
#0
STA
MSBX
;Store in
MSBX to locate
sprite in
horizontal
locations to
LDX
#0
255 only
LDA
#70
;Vertical
location
STX
SP0X
; Increment
horizontal
STA
SP0Y
location by X
;Y register stays
at constant
location of A
LDY
INY
#0
register
; Delay loop.
CPY
#255
BNE
PAUSE
INX
CPX
#254
BNE
MOVE
RTS
258
KIDS' ASSEMBLER - SPRTTE-0 STARTER
ADRS
OPCODE OPERAND
49152
JSR
$E544
49155
LDA#
$0D
49157
STA
$7F8
49160
LDA#
1
49162
STA
$D015
49165
LDA#
2
49167
STA
$D027
49170
LDX#
49172
LDA#
49174
STA-X
$0340
49177
INX
49178
CPX#
63
49180
BNE
49174
49182
LDX#
49184
LDA#
$FF
49186
STA-X
$0340
49189
INX
49190
CPX#
63
49192
BNE
49186
49194
LDA#
49196
STA
$D010
49199
LDX#
49201
LDA#
70
49203
STX
$D000
49206
STA
$D001
49209
LDY#
49211
INY
49212
CPY#
255
49214
BNE
49211
49216
INX
49217
CPX#
254
49219
BNE
49203
49221
RTS
If you're using the Kids' Assembler, you're at a disadvantage
since you cannot define the descriptive variable names. However,
259
with the Merlin and the Commodore assemblers, it's a good idea to
create a source code with all of the sprite registers defined and then
save it to disk. In this way, when you're ready to work with sprites,
all of your variables are defined and you can start with the creation
and movement of your sprites with little effort.
SPRITE BUILDING
The most exacting process in
sprite programming is building
the sprites. We'll design and
build a sprite (something more
interesting than a cross or block)
and examine the different ways
your assembler can store the
sprite in memory. We will use
Block 13 (832-895) in all of our
examples. Instead of using the
Kids' Assembler, we'll write a
Sprite Assembler that will be a
lot easier for creating and stor-
ing sprites. Our sprite will be
called PLANET DESTROYER!
Making sprites takes
Patience...
PLANET DESTROYER
Column 1
Column 2
Column 3
Row
xxxxxxxx
XXXXXXXX
xxxxxxxx
1
xxxxxxxx
xxxxxxxx
xxxxxxxx
2
xxxxxxxx
xxxxxxxx
xxxxxxxx
3
xxxxxxxx
xxxxxxxx
xxxxxxxx
4
xxxxxxxx
xxxxxxxx
xxxxxxxx
5
+XXXXXXX
xxxxxxxx
xxxxxxxx
6
+ + +XXXXX
xxxxxxxx
xxxxxxxx
7
++++++XX
xxxxxxxx
xxxxxxxx
8
++++++++
+XXXXXXX
xxxxxxxx
9
++++++++
+ + + + xxxx
XXX + + + XX
10
XXX+ + + + +
++++++++
+ + + +XXXX
11
XX+ + + + + +
++++++++
++++++++
12
260
XX++++++
++++++++
++++++++
13
XXX+++++
++++++++
+ + + +XXXX
14
++++++++
+ + + +XXXX
xxxxxxxx
15
++++++++
+XXXXXXX
xxxxxxxx
16
+ + + + + +xx
xxxxxxxx
xxxxxxxx
17
+ + +XXXXX
xxxxxxxx
xxxxxxxx
18
+XXXXXXX
xxxxxxxx
xxxxxxxx
19
xxxxxxxx
xxxxxxxx
xxxxxxxx
20
xxxxxxxx
xxxxxxxx
xxxxxxxx
21
PLANET DESTROYER
Column 1
Column 2
Column 3
Row
00000000
00000000
00000000
1 0,0,0
00000000
00000000
00000000
2 0,0,0
00000000
00000000
00000000
3 0,0,0
00000000
00000000
00000000
4 0,0,0
00000000
00000000
00000000
5 0,0,0
100000000
00000000
0000000
6 128,0,0
1110000
00000000
00000000
7 224,0,0
11111100
00000000
00000000
8 252,0,0
11111111
10000000
00000000
9255,128,0
11111111
11110000
00011100
10 255,240,28
00011111
11111111
11110000
11 31,255,240
00111111
11111111
00111111
12 63,255,63
00111111
11111111
00111111
13 63,255,63
00011111
11111111
11110000
14 31,255,240
11111111
11110000
00000000
15 255,240,0
11111111
10000000
00000000
16 255,128,0
11111100
00000000
00000000
17 252,0,0
11100000
00000000
00000000
18 224,0,0
100000000
00000000
0000000
19 128,0,0
00000000
00000000
00000000
20 0,0,0
00000000
00000000
00000000
21 0,0,0
At this point we have all the values for our PLANET
DESTROYER sprite. The most obvious (and tedious) way of get-
ting those values into memory would be to do something like the
following:
261
LDA #0
STA 832
LDA #0
STA 833
etc...
With the Commodore and Merlin assemblers, a better way
would be to define them with special pseudo-opcodes (directives).
Remember when we used ASC to define strings? Well, you can also
define numbers, except that you use DFB on Merlin and .BYTE on
the Commodore EDITOR64. These directives work something like
DATA statements. Each value is separated by a comma. For exam-
ple, the following routine would read in the values in the line labeled
DATA (any name will do).
LDX #0
BUILD
LDA DATA.X
STA 832,X
INX
CPX #64
BNE BUILD
{Commodore}
DATA
.BYTE 0,0,0,0,0,0,0
.BYTE 128,0,etc...
{Merlin}
DATA
DFB 0,0,0,0,0,0,0
DFB 128,9,etc...
Now let's type in our PLANET DESTROYER program and give
it a test run.
GENERAL - PLANET DESTROYER
LABEL OPCODE OPERANDCOMMENT
{Merlin ORG $8000}
{Commodore *=$C000}
262
********************************************
PLANET DESTROYER
* *
,***************************************•********
{Commodore}
SPRITE©
ENABLE
COLOR©
SP0X
SPOY
MSBX
SHOUSE
{Merlin}
SPRITE©
ENABLE
COLOR©
SP0X
SPCY
MSBX
SHOUSE
CLEANUP
= $7F8
= $D©15
= $D©27
= $D©0©
= $D©01
= $D01©
= $©34©
EQU
EQU
EQU
EQU
EQU
EQU
EQU
JSR
LDA
STA
STA
LDA
STA
LDA
STA
LDA
STA
LDX
LDA
STA
INX
CPX
$7F8
$D©15
$D©27
$D0©1
$D01©
$©34©
$E544
#© ;Color for black
$D©2© ; Border black
$D©21 ;Background
black
#$©D
SPRITE©
#1
ENABLE
#7 ;Yellow
COLOR©
#©
SHOUSE.X
#64
263
BNE
CLEANUP
LDX
#0
BUILD
LDA
DATA,X
;Read DFB /
.BYTE values
one at a time
STA
SHOUSE,X
;Store in
SpriteHOUSE
INX
CPX
#63
BNE
BUILD
LDA
#0
STA
MSBX
LDX
#0
LDA
#70
MOVE
STX
SP0X
STA
SP0Y
LDY
#0
PAUSE
INY
CPY
#255
BNE
PAUSE
INX
CPX
#254
BNE
MOVE
JMP
MOVE
RTS
;Use .BYTE instead of DFB with Commodore
DATA
DFB
0,0,0,0,0,0,0,0,0
DFB
0,0,0,0,0,0,128,0,0
DFB
224,0,0,252,0,0
DFB
255,128,0
DFB
255,240,28
DFB
31,255,240
DFB
63,255,63
DFB
63,255,63
DFB
31,255,240
DFB
255,240,0
DFB
255,128,0
DFB
252,0,0
264
DFB 224,0,0
DFB 128,0,0
DFB 0,0,0,0,0,0
Notice that when using DFB and .BYTE directives, the label,
DATA, is able to reference all of the values entered. This includes
values in lines after the label.
KIDS' SPRITE ASSEMBLER
As you can imagine, building sprites with the Kids's Assembler
would be a royal pain in the neck since it has no labels or DFB /
.BYTE directives. Therefore, let's make a special assembler that
will make it easy to enter sprite data and save the sprite to disk.
Then, once you have your sprite made, you can load it up into
memory without having to build it each time you want to use it. In
this way you can create a whole library of sprites, and then just
write the programs to enable and move the sprites.
Since the Sprite Assembler is designed especially for sprites and
not general assembly work, there are some special features about it.
First, it takes data in groups of three. Each INPUT prompt expects
three values separated by a comma. For example, you will be pro-
mpted,
ROW 5?
and you should enter something like,
ROW 5 ? 255,128,0 {RETURN}
By using the method we have developed for calculating sprite values
in the sprite matrix, it will be very simple to read sprites into
memory.
When the program starts, you will be expected to give one of four
blocks. The block number, not the beginning address is to be
entered. When the program is saved to disk, the block number is ap-
pended to the file name so that later when you want to use that
265
sprite in a program, you will know the block number it uses. When
you want the sprite in memory to be used by a program, you will
load the sprite as follows:
LOAD "FILENAME 13",8,1
Then when the program ac-
cesses the data in Block 13, or
whatever block it was saved in, it
will find your sprite.
Sprite Assembler
Sprite Assembler
10 PRINT CHR$(147):R = 1
20 PRINT "STARTING ADDRESS AS BLOCK:"
30 PRINT "BLOCK 11: 704-767"
40 PRINT "BLOCK 13: 832-895"
50 PRINT "BLOCK 14: 896-959"
60 PRINT "BLOCK 15: 960-1023"
70 FOR X = 1 TO 39
80 PRINT CHR$(18);CHR$(32); : NEXT : PRINT
90 INPUT "BLOCK NUMBER";BL
100IFBL <> 11 ANDBL <>13ANDBL <> 14
ANDBL <> 15 THEN 90
110 SA = BL*64
120 FOR X = SA TO SA + 62 STEP 3
130 PRINT "ROW";R
140 INPUT A,B,C
150 IF A > 255 OR B > 255 OR C > 255 THEN 130
160 POKE X,A : POKE X + 1,B : POKE X + 2,C
170 R = R + 1 : NEXT
200 REM *************
210 REM WRITE TO DISK
OOQ\ RFM *************
230 LB = SA- INT(SA/25^ * 256
266
240 HB = INT(SA/256)
250 INPUT "ENTER SPRITE NAME";SN$
260 SN$ = "0:" + SN$ + STR$(BL) + ",P,W"
270 OPEN2,8,2,SN$
280 PRINT#2,CHR$(LB) + CHR$(HB)
290 FOR V = SA TO SA + 62 : SC = PEEK(V)
300 PRINT#2,CHR$(SC)
310 NEXT V
320 CLOSE2
We should do the same thing for the other assemblers. After all,
if we can save sprites as separate files, then we won't have to pro-
gram around them. We can just load them into memory and then
access them from any sprite program we write. The main thing to
remember is that we have to use the available blocks of memory to
store our sprites. Therefore the ORG must begin in one of those
blocks. The following shows how to do this with Block 13. The
same technique is used with the other blocks. Just change the ORG
or * = address.
GENERAL - SPRITE CREATOR 13
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $0340}
{Commodore *=$0340}
********************************************
* *
SPRITE CREATOR 13
* *
********************************************
{Commodore}
SPRITE© =$7F8
ENABLE =$D015
COLOR0 = $D027
SP0X =$D0
SP0Y =$D001
MSBX =$D010
SHOUSE =$0340
267
{Merlin}
SPRITE© EQU $7F8
ENABLE EQU $D015
COLOR© EQU $D027
SP0X EQU $D000
SP0Y EQU $D01
MSBX EQU $D010
SHOUSE EQU $0340
********************************************
* *
* Build Sprite
* *
********************************************
BUILD
LDX
#0
LDA
DATA.X ;Read DFB /
.BYTE values
one at a time
STA
SHOUSE.X
;Store in
SpriteHOUSE
INX
CPX
#63
BNE
BUILD
********************************************
*
*
Sprite Data
*
*
********************************************
{Merlin}
DATA DFB #,#,# ;Values for
sprite
{Commodore}
DATA .BYTE #,#,#
268
All you have to do is to enter your sprite data after the DFB or
.BYTE directives. When you're finished, save the object code to
disk, and then using either the LOADER64 programs on the Com-
modore or the LOAD "SPRITENAME.O",8,l sequence for the
Merlin object file.
= =THE WORM HAS TURNED= =
The Sprite Assembler is so convenient, you may actually
want to use it instead of your regular assembler, especially
if you're using the Commodore assembler. This is
because you can use the LOAD "FILENAME",8,1 se-
quence to load sprites created with the Sprite Assembler.
You can't do that with the Commodore system unless
you create and save code with the monitor. So here's a
case where the worm has turned and it's actually easier to
use something from the Kids' system.
Now that we have programs that will create sprites and save them
for use, we will need to see how to access these sprites from within a
program. We'll just modify our starter program to be a Sprite
Tester. We can load a sprite from disk, and then run the following
program to see if the sprite is what we thought it was. Notice that we
have taken out the CLEANUP subroutine. This is because that
would wipe out any sprite in memory we wanted to test. This tester
is designed for sprites using Block 13, but it can be changed to test
sprites in any block. Use the following sequence:
1. Create your sprite and save it to disk.
2. Load your sprite into memory as you would any other
machine language program.
3. Load the SPRITE TESTER program into memory and SYS
it.
Your sprite should run across the screen for you.
269
GENERAL - SPRITE TESTER
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $8000}
{ Commodore * = $C©0© }
• ******* VHRWV1
. *
. *
. *
rwwwwwwwwwwwwwirwww
SPRITE TESTER
. ************************ *'* * * * *
{CommOodore}
SPRITE©
= $7F8
ENABLE
= $D015
COLOR©
= $D027
SP0X
= $D00©
SP0Y
= $D©01
MSBX
= $D©1©
SHOUSE
= $©34©
{Merlin}
SPRITE©
EQU
$7F8
ENABLE
EQU
$D015
COLOR©
EQU
$D027
SP©X
EQU
$D000
SP©Y
EQU
$D001
MSBX
EQU
$D©10
SHOUSE
EQU
$©34©
JSR
$E544
LDA
#©
STA
$D©20
STA
$D021
LDA
#$©D
STA
SPRITE©
LDA
#1
STA
ENABLE
LDA
#7
STA
COLOR©
LDX
#©
270
MOVE
PAUSE
LDA
#$00
STA
MSBX
LDA
#70
STX
SP0X
STA
SP0Y
LDY
#0
INY
GPY
#255
BNE
PAUSE
INX
CPX
#254
BNE
MOVE
RTS
KIDS' ASSEMBLER - SPRITE TESTER
ADRS OPCODE OPERAND
49152
JSR
$E544
49155
LDA#
49157
STA
$D021
49160
STA
$D020
49163
LDA#
$0D
^ItoBSk*.
49165 STA$7F8
Jnk
49168
49170
LDA#
STA
1
$D015
Kw/y^^'^^ T tfSfflWBB
49173
LDA#
7
WW&i&wvWv
49175
STA
$D027
^Mffill w
49178
LDX#
^wMtifflW
49180
LDA#
\IHBK7
49182
STA
$D010
wKff/
49185
LDA#
70
IMf
49187
STX
$D000
j^Mii^t
49190
STA
$D001
vr*mflMkW
49193
LDY#
^"
49195
INY
Testing Sprite
49196
CPY#
255
49198
BNE
49195
49200
INX
49201
CPX#
254
271
49203
49205
BNE
RTS
49187
FULL HORIZONTAL MOVEMENT
So far, we have only moved part of the full horizontal move. In
order to move our full horizontal width, we have to store a 1 in the
MSBX register. That's easy enough. Just insert the following lines
to the SPRITE TESTER program before the RTS and your sprite
will move across the entire screen:
MOVE2
PAUSE2
LDX
#0
LDA
#1
STA
MSBX
;Set the high
byte of X
position
LDA
#70
;Keep Y at 70
STX
SP0X
;X position is
now 255 + X
STA
SP0Y
LDY
#0
INY
CPY
#255
BNE
PAUSE2
INX
CPX
#90
BNE
MOVE2
When you test a sprite on the program now, it will go all the way
across the screen.
SPRITE EXPANSION
A final aspect of sprite programming we will discuss is the expan-
sion of sprites to double size. The registers at $D01D (53277) and
$D0 17 (53271) control the X and Y sizes of the sprite. The sprites we
have been using so far have been single width and height. By storing
the Sprite Value in the respective registers, it is possible to expand
the sprite to double width. The following sprite values are used:
272
Sprite Value
Sprite
1
Sprite 1
2
Sprite 2
4
Sprite 3
8
Sprite 4
16
Sprite 5
32
Sprite 6
64
Sprite 7
128
It is fairly important to have a variable name for your X and Y
expansion registers. This is so that you can add and subtract the
sprite value to those registers to change the sprite size. If you're
using multiple sprites, this becomes crucial. For example, if you
store 1 in $D017 and $D01D, Sprite will be expanded. If you then
STA 2 in the same registers so that Sprite 1 will be expanded, Sprite
will be reset to normal size. The following shows how to change
sprite size using multiple sprites.
YXPAND
EQU
$D017
XXPAND
EQU
$D01D
LDA
#1
;Sprite value stored
in expansion
registers
STA
YXPAND
STA
XXPAND
LDA
YXPAND
CLC
ADC
#2
;Add Sprite 1 value to
registers
STA
YXPAND
STA
XXPAND
Now if you want to return one or the other sprites to normal size,
you simply subtract from the expansion registers. For example, to
keep Sprite 1 at double size and return Sprite to normal, you
would just SBC 1 from each register.
273
LDA XXPAND
SEC
SBC #1
STA YXPAND
STA XXPAND
There is still more to sprites than we have covered, but we have
dealt with the major aspects of assembly language programming
and sprites. The trick is to get organized with sprites. Then, it is
simply a matter of following a series of steps. By using the keyboard
and joystick subroutines we've developed, it is possible to control
movement from these external devices. Since you should already
know how to do that, I'll let you insert these subroutines yourself.
ASSEMBLY SOUNDS
Like sprite programming in BASIC, most sound programming is
also done with POKEs and PEEKs. Therefore, it should not be at
all difficult to do make all the sounds we want. The trick, as with
sprites, is to organize everything into variables pertaining to the pro-
per registers.
We're going to look at the basic features of sound on your Com-
modore 64. By making changes to the program we're going to
write, you will be able to produce a whole lot of sound. By changing
the variables, you will be able to make music, racket or the sound of
a train rushing through your TV. Here, the trick is to see how to get
your sounds working in assembly language. Other sources are
available for getting all of the several aspects of your SID chip
working. Just in case you have not worked with sound before, turn
up the sound on your TV so you can hear what's going on. (If you
have a monitor with no sound, you might as well skip this section.)
On the most fundamental level, you computer deals with six
registers to create sound. The duration of a sound is done with a
delay loop that keeps everything going until the program nulls the
sound registers by filling them with zeros.
274
1. Volume Control. The volume control register is at $D418
(54296). It can have a maximum value of $F (15). It controls how
loud the sound will be.
2. Attack - Decay. This refers to how fast a sound reaches its
maximum volume and falls from that volume. Its register is at
$D405 (54277). The maximum value is 240.
3. Sustain - Release. The extent to which a sound is carried at a
certain level before it is released is its sustain/release variable. The
$D406 (54278) register holds it. Like attack/decay, its maximum
value can be 240.
4. High and Low Frequency. The pitch of notes is determined by
their high/low frequency. The low frequency register is at
$D400and the height at $D401.
5. Waveform. Sounds can either be smooth or rough. The
waveform determines how this will occur. There are four major
waveforms we can use:
a. Triangle =17
b. Sawtooth = 33
c. Pulse = 65
d. Noise = 129
To make a sound, we first store values in the registers. Then we
simply "hold" a sound with a delay loop. Even a loop of 255 will
give you a very short sound; so you may need nested loops, depen-
ding on what sound you want. At the end of the delay loop, you will
null the registers by storing zeros in them.
GENERAL - ASSEMBLY SOUND
LABEL OPCODE OPERAND COMMENT
{Merlin ORG
{Commodore * = $CO0O}
275
. *
WTTWWWWWWWT
[KKXKKKHKK
H » W W H H W it H W » M W
*
. *
ASSEMBLY SOUNDS
*
*
. ********************************************
{Commodore}
SIGVOL
= $D418
t
ATDCY1
= $D405
SUREL1
= $D406
VCREG1
= $D404
FREL01
= $D400
FREHI1
= $D401
{Merlin}
SIGVOL
EQU
$D418
ATDCY1
EQU
$D405
SUREL1
EQU
$D406
VCREG1
EQU
$D404
FREL01
EQU
$D400
FREHI1
EQU
$D401
LDA
#15
STA
SIGVOL
;Set volume to
15
LDA
#128
STA
ATDCY1
;Set attack
- decay to 128
STA
SUREL1
;Set sustain
- release to 128
LDA
#195
STA
FREL01
;Store 195 in the
low frequency
LDA
#16
STA
FREHI1
;Store 16 in the
high frequency
LDA
#17
STA
VCREG1
LDY
#0
SET
LDX
#0
PLAY
I NX
276
CPX
#255
;Double delay
loop to play
sound
BNE
PLAY
INY
CPY
#100
BNE
SET
LDA
#0
;Null registers
with©
STA
VCREG1
STA
ATDCY1
STA
SUREL1
STA
FREL01
STA
FREHI1
RTS
KIDS' ASSEMBLER - ASSEMBLY SOUNDS
ADRS OPCODE OPERAND
49152
LDA#
15
49154
STA
$D418
49157
LDA#
128
49159
STA
$D405
49162
STA
$D406
49165
LDA#
195
49167
STA
$D400
49170
LDA#
16
49172
STA
$D401
49175
LDA#
17
49177
STA
$D404
49180
LDY#
49182
LDX#
49184
INX
49185
CPX#
255
49187
BNE
49184
49189
INY
49190
CPY#
100
49192
BNE
49182
49194
LDA#
277
49196
STA
$D404
49199
STA
$D405
49202
STA
$D406
49205
STA
$D400
49208
STA
$D401
49211
RTS
You should try changing everything from the size of the delay
loop to the values stored in the registers. By doing so, you will
discover the sounds you need and hear ones that will surprise you.
Combine the sounds with your sprites and you'll have the makings
of an arcade game!
SUMMARY
This chapter is important in that we used two special chips and
sets of registers in your Commodore 64. With the VIC chip, we
were able to access sprite graphics. This allowed us to produce and
move special characters we can create ourselves. Secondly, we used
the registers in the SID chip to create sound. The combination of
these two sets of registers in single programs will let you do some
very interesting things. In the next chapter, we'll look at some ex-
amples.
278
CHAPTER 15
DOWN THE ROAD
Introduction
This last chapter is to help you go on in assembly language once
you're finished here. Like any other language, programming or
spoken, the real secret is to use it. If you study German in school,
for example, and you never use it, you'll forget it. On the other
hand, if you live in Germany and are constantly speaking German,
soon you will become fluent. The same is true with assembly
language. When you sit down to write a program, even in BASIC,
ask yourself, "How could the same thing be done in assembly
language?" You can start off with little subroutines to speed up
your BASIC programs and work your way up to full blown pro-
grams, all written with an assembler.
Practice will make perfect, but you still have a lot of new opcodes
to learn with which you can practice. We will look at some tricks to
help you learn new opcodes.
Finally, you're going to need more materials to study to learn
assembly language. For the Commodore 64, there are some
available materials that are highly recommended. We'll look at
those so that you can keep progressing beyond this point.
279
MERGING SUBROUTINES
Assembly language programming is best digested as a 50 course
dinner. If you take just a little at a time, you will be able to consume
everything. These little bites are best conceived as "subroutines."
This actually involves writing little programs that do something as
we have done in this book. Each little program, when merged with
other little programs, becomes a big program.
To see how to append programs, we will have to use the Merlin
and Commodore assemblers as examples since they both can ap-
pend programs and the Kids' Assembler cannot. That means, that
you can load one source code into the editor and then tack on
another source code to it.
Appending With Merlin
To append one source code to another, first load one source code
using the "L" instruction from the EXECUTIVE MODE. Once
the first file is loaded return to the EXECUTIVE MODE by press-
ing 'Q' from the editor, and use the "A" option to append the se-
cond file.
Appending with Commodore EDITOR64
Once the editor is in memory, use the GET command to load the
first file. All files loaded with GET begin at line 1000. List your
program to see the highest line number. For example, let's say that
the highest line number in your program is 1200. To append a pro-
gram to the one in memory, use the GET command with the
parameter for the first line number. That line number must be
higher than 1200. For example, we'll append FILE 2 to FILE 1.
1. GET "FILE 1" {RETURN}
2. LIST {RETURN}
Highest line number is 1200
3. GET "FILE 2",1210
Now FILE 2 would be appended to FILE 1.
280
To do something new with the programs we have developed in
this book, let's append some. In the last chapter we made a "Planet
Destroyer" sprite and "Assembly Sounds." Let's put those two
together, edit them and see what we get. Load "Planet Destroyer"
and then append "Assembly Sounds." We'll look at the program
first and then explain what editing changes were made to make it.
(We'll use the Merlin listing since the only difference is in the for-
matting of EQUates, and with the large number of variables defin-
ed, having two sets would be confusing.)
GENERAL SLOW NOISY SPACE SPRITE
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $0000}
{Commodore *=$C000}
*********************************************
* *
NOISY SPRITE ROCKET
* *
*********************************************
SPRITE©
EQU
$7F8
ENABLE
EQU
$D015
COLOR0
EQU
$D027
SP0X
EQU
$D000
SP0Y
EQU
$D001
MSBX
EQU
$D010
SHOUSE
EQU
$0340
SIGVOL
EQU
$D418
ATDCY1
EQU
$D405
SUREL1
EQU
$D406
VCREG1
EQU
$D404
FREL01
EQU
$D400
FREHI1
EQU
$D401
HI
EQU
$C300
WHY
EQU
$C304
EX
EQU
$C306
281
;SPRITE SUBROUTINE
. ********************************************
JSR
$E544
LDA
#$©D
STA
SPRITE©
LDA
#1
STA
ENABLE
LDA
#2
STA
COLOR©
LDX
#©
LDA
#$©0
CLEANUP
STA
INX
SHOUSE,X
CPX
#64
BNE
CLEANUP
LDX
#©
BUILD
LDA
DATA.X
STA
SHOUSE.X
INX
CPX
#63
BNE
BUILD
LDA
#©
STA
MSBX
LDX
#©
STX
EX
LDA
#70
STA
WHY preserve "Y"
MOVE
STX
SP©X
LDA
WHY
STA
SP0Y
INC
EX ; Increment X
through EX
LDX EX
SOUND SUBROUTINE
NO PAUSE LOOP SINCE THE SOUND SLOWS
DOWN MOVEMENT
********************************************
LDA #1©
STA HI
282
START
LDA
#15
STA
SIGVOL
LDA
#128
STA
ATDCY1
STA
SUREL1
LDA
HI
STA
FREL01
LDA
HI
STA
FREHI1
LDA
#17
STA
VCREG1
LDY
#0
SET
LDX
#0
PLAY
INX
CPX
#20
BNE
PLAY
INY
CPY
#20
BNE
SET
LDA
#0
STA
VCREG1
STA
ATDCY1
STA
SUREL1
STA
FREL01
STA
FREHI1
INC
HI
LDA
HI
CMP
#40
BNE
START
LDX
EX
CPX
#254
BNE
MOVE ;Back to Sprite
;END OF PROGRAM AND SPRITE DATA
*********************************************
RTS
DATA DFB 0,0,0,0,0,0,0,0,0
DFB 0,0,0,0,0,0,128,0,0
DFB 224,0,0,252,0,0
DFB 255,128,0
283
DFB
255,240,28
DFB
31,255,240
DFB
63,255,63
DFB
63,255,63
DFB
31,255,24©
DFB
255,240,0
DFB
255,128,0
DFB
252,0,0
DFB
224,0,0
DFB
128,0,0
DFB
0,0,0,0,0,0
If you look at the sprite data, it's the same we used with the
Planet Destroyer sprite. You can change it to any sprite configura-
tion you want, and using your append features of your assembler, it
is easy. The main changes we made were in the form of substitution.
Instead of using the INX instruction to increment the horizontal
position of the sprite, we stored the X position in the 'variable'
(location) we called EX and incremented the value in EX. We also
used a variable, WHY, to store the vertical position of the sprite.
The reason we did that is because the X and Y registers were heavily
used in producing our sound. Since the sound values were not
directly related to the movement values of X and Y, we needed
some way to preserve the X and Y values in movement. By only
using the values in EX and WHY in the movement segment and
resetting the X and Y registers in the sound portion of the program,
we were able to keep everything moving and sounding the way we
intended.
While the actual movement mechanism in not tied to the sound,
the speed of the movement is. If the various sizes of the loops in the
sound segment of the program are shortened, the sound will change
and so will the speed of the rocket. The way it is now, it makes a
"space gurgle" and creeps along at a snail's pace. (Actually, it's
sneaking up on a planet to destroy.)
284
Finally, you probably noticed that all of the EQUates are
together at the beginning of the program. This was done with the
MOVE function on Merlin. With the Commodore 64 Macro As-
sembler Development System, you can do the same thing by chang-
ing the line numbers of the equate ( = ) directives. Then, delete the
old line numbers and everything is moved.
APPENDING AND INSERTING SUBROUTINES
In the above program, we essentially stuck two programs
together; one on top of the other. Now, we'll do something a little
more complex. We'll start the same way, though. First, load
"Planet Destroyer" and then append "Joystick Draw." What
we're going to do is to insert the joystick read subroutine inside the
sprite routine. Take out the routine to increment the X register to
move, and in its place put the joystick read subroutine. Then,
change the JMPs to PRINT to JMPs to MOVE. Both the Merlin
and Commodore editors have CHANGE commands you can use to
do this quickly.
We added another wrinkle to our joystick program. When the
values in EX and WHY reach the limits of and 255, we don't want
them to "turn over." Instead, we just have them jump to the
routine that will increment them or decrement them so they stay
within the registers' boundaries. Thus, when UP or LEFT is 0, the
program will JMP to DOWN or RIGHT. Similarly, when DOWN
or RIGHT reaches 255, the value is decremented by JMPs to UP
and LEFT.
LABEL OPCODE OPERAND COMMENT
{Merlin ORG $C000}
{Commodore *=$C000}
*********************************************
* *
JOYSTICK SPRITE
* *
*********************************************
285
CLEAR
EQU
$E544
JSTICK
EQU
OFSET
EQU
$C300
FIRE
EQU
$C302
EX
EQU
$C304
WHY
EQU
$C306
SPRITE©
EQU
$7F8
ENABLE
EQU
$D015
COLOR©
EQU
$D027
SP0X
EQU
$D©00
SPOY
EQU
$D0©1
MSBX
EQU
$D010
SHOUSE
EQU
$©34©
. ********************************
JSR
CLEAR
LDA
#$0D
STA
SPRITE©
LDA
#1
STA
ENABLE
LDA
#2
STA
COLOR©
LDX
#©
LDA
#$00
CLEANUP
STA
SHOUSE.X
INX
CPX
#64
BNE
CLEANUP
LDX
#©
BUILD
LDA
DATA.X
STA
SHOUSE.X
INX
CPX
#63
BNE
BUILD
LDA
#0
STA
MSBX
LDA
#$FF
STA
OFSET
LDA
#16
$DC01
286
STA
FIRE
LDA
#40
STA
EX
;Starting X
position of
Sprite
STA
SP0X
LDA
#70
STA
WHY
;Starting Y
position of
Sprite
STA SPOY
;READ THE JOYSTICK
. ********************************************
START
PAUSE
UP
LDA
JSTICK
EOR
OFSET
CMP
#1
BEQ
UP
CMP
#2
BEQ
DOWN
CMP
#4
BEQ
LEFT
CMP
#8
BEQ
RIGHT
CMP
FIRE
BEQ
END
LDX
#0
INX
CPX
#254
BNE
PAUSE
JMP
START
DEC
WHY
LDY
WHY
CPY
#0 ;Check for
adjustment
BEQ DOWN
287
DOWN
LEFT
RIGHT
END
JMP
MOVE
; Branch to
MOVE
subroutine
INC
WHY
LDY
WHY
CPY
#255
BEQ
UP
JMP
MOVE
DEC
EX
LDX
EX
CPX
#0
BEQ
JMP
RIGHT
MOVE
INC
EX
LDX
EX
CPX
#255
BEQ
LEFT
JMP
MOVE
RTS
;MOVEMENT OF SPRITE
********************************************
MOVE
STX
SP0X
STY
SP0Y
LDX
#0
M PAUSE
INX
CPX
#254
BNE
M PAUSE
JMP
START
;SPRITE DATA
. ********************************************
DATA
DFB
0,0,0,0,0,0,0,0,0
DFB
0,0,0,0,0,0,128,0,1
DFB
224,0,0,252,0,0
DFB
255,128,0
DFB
255,240,28
DFB
31,255,240
DFB
63,255,63
288
DFB
63,255,63
DFB
31,255,240
DFB
255,240,0
DFB
255,128,0
DFB
252,0,0
DFB
224,0,0
DFB
126,0,0
DFB
0,0,0,0,0,0
These two programs should give you an idea of how to merge two
smaller routines into a single larger one. Now you can treat the
larger routines as subroutines themselves. You have a subroutine
that will move your sprites and create sound and one that will move
sprites with your joystick. By appending different sprite data, ad-
ding additional sprites, and appending other subroutines you can
build larger programs. The trick is to build a library of subroutines,
and instead of starting from scratch every time you sit down to pro-
gram, you can start with a substantial amount of the work already
done and debugged. This also points to the importance of getting a
good assembler. While the Kids' Assembler is fine for learning
assembly language programming and small programs, it becomes
increasingly difficult to use as your programs increase. Starting all
over each time you program is a waste of time, and in the long run
you will not learn as much since more effort is used keying in old
code than creating new code.
GETTING TO KNOW THE OTHER OPCODES
We've covered the most commonly used opcodes and addressing
modes, but there are still more to learn and use. The best way to
learn how to use new opcodes is to first isolate them, and then test
them with simple programs. Often novice assembly language pro-
grammers will insert a new opcode into a large program, and while
it may or may not work, it is difficult to see why it did or did not.
Usually the best way to see if an opcode does what you think it
does is to put it into a routine that will put a value into the ac-
cumulator. Then have that value sent to the screen. If the screen
character is what you expect; then you can assume that the opcode
did what you thought. In the listing in Appendix A, the Kids'
289
Assembler there has all of the opcodes for the 6510, and Appendix
B has an alphabetical listing of all 6510 opcodes.
A good book on 6502 opcodes (which are identical to the
6510opcodes) is:
6502 Assembly Language Programming
By Lance A. Leventhal
Berkeley, CA : Osborne/McGraw-Hill
This book describes in technical detail what each code does.
RESOURCES FOR LEARNING MORE ABOUT ASSEMBLY
LANGUAGE PROGRAMMING ON THE COMMODORE 64
As more good assemblers are becoming available for the Com-
modore 64, there should be more resources for learning assembly
language programming. Most assembler manuals have very little
about programming. They usually just explain how to use the
assembler, more or less assuming you already know how to pro-
gram. However, from what you know now, you should be able to
understand what the assembler manuals are talking about and use
them to some advantage.
Since there are a wide variety of resources for learning assembly
language, we'll divide them into several categories. First, we'll look
at reference manuals. These are books used to look things up.
Secondly, we'll see what other books there are on "how to" do
assembly language programming. Third, we'll look at magazines
you might read to either learn assembly language programming or
see some examples of assembly language programs. Before all of
that, though, we'll look at the best resource possible, Commodore
64 User Groups.
USER GROUPS
The most valuable tips, techniques and education you can receive
for assembly language on the Commodore 64 is from a user group.
These are people who own Commodore 64's and get together to
share their common interest. These groups typically have several
290
members who are kids like yourself. (My group even has a lot of
adults who are just like kids.) If you attend meetings in your area,
not only will you meet other kids who have Commodore 64's, you
can learn a lot about assembly language programming by just ask-
ing.
Now you may wonder why people are going to give you all of this
free information. It's simple; assembly language programmers love
to brag. I do it all the time. If someone asks about an assembly
language procedure, nothing makes me feel better than being able
to answer their question. If you know BASIC, you've probably ex-
perienced the same thing. You can help someone by showing off!
What could be better? Not only will you learn something, you'll
make someone else feel very good about themselves.
In addition to getting a wealth of information, you can also gain
access to public domain (that means FREE!) software. Since a good
deal of public domain software is written in assembly language, you
can use assembly listing to see how others write assembly code on
the Commodore 64.
REFERENCE BOOKS
There are two reference books no assembly language program-
mer should be without. First, there is;
Commodore 64 Programmer's Reference Guide
Commodore Business Machines, Inc.
And Howard W. Sams & Co., Inc.
This book has all of the technical details you will need to work your
Commodore 64. Most important is its description of the Kernal
routines in your machine.
Secondly, your assembly language programming will be greatly
enhanced by the book,
Mapping the Commodore 64
By Sheldon Leemon
Greensboro, N.C. : COMPUTE! Publications, Inc., 1984
291
All of the built-in subroutines in your Commodore 64, along with
substantial explanations of their use are contained in this book. It is
well written, and there are several example programs in BASIC that
illustrate how the various subroutines work.
HOW-TO BOOKS
There really are not a lot of books available for assembly
language programming on the Commodore 64 right now.
However, mere are three books you might want to examine for the
transition from BASIC to machine/assembly language.
The Intermediate Commodore 64
ByGuyGrotke
Chatsworth, CA : Datamost, 1984
This book builds a bridge between BASIC and assembly
language programming. Some of the more advanced aspects of
graphics, files and basic assembly language procedures are explain-
ed. There are a number of listings of assembly language routines
and the appendix has all of the decimal and hexadecimal values for
the 6510 opcodes.
Commodore 64 Exposed
By Bruce Bayley
Melbourne, Australia: Melbourne House, 1983
Like the Intermediate Commodore 64, this is a "transition
book" between BASIC and more advanced techniques. There are
some assembly language listings, a good memory map and lots of
good tips.
Inside the Commodore 64
By Don French
Cannon Falls, MN : French Silk
This is actually the manual that accompanies the French Silk
Assembler. More than most manuals, this one goes beyond just ex-
plaining how to use the assembler. There are several example pro-
292
grams, an excellent memory map, and you can get it with the
assembler or without.
MAGAZINES
The problem with most magazines for the Commodore 64 is that
they create machine code with BASIC programs. As a result, their
listings are in the form of READ/DATA statements that are POK-
Ed into memory. You can't see the assembly opcodes, but rather
you just get a pile of decimal machine codes. However, there are
some assembly listings, and you can still learn something about
machine language programs even if the listings are given in BASIC.
With Merlin's Sourceror, you can create source code from the
machine code generated with the BASIC programs in the
magazines. Thus, with the right tools, you can extend your
knowledge a good deal.
The most likely source for actual assembly listings is in Com-
mander magazine. Like most other magazines, this one will have
machine code generated by BASIC programs, but it also has
disassembled listings as well. Previous issues have contained series
articles, such as "Explorations With Assembly Language" by Eric
Giguere. You'll find plenty of new material just about every month
in the Commander.
A second source for machine code is in COMPUTE! 's Gazette.
There's a regular series, "Machine Language For Beginners" by
Richard Mansfield you will find useful. In previous articles, there
have been assembly listings for various aspects of machine language
techniques. Mansfield handles the material in small, clear chunks,
and you can get a lot here.
Third, RUN: The Commodore 64 & VIC 20 Magazine, has a
number of programs and articles dealing with machine and
assembly language programming. Several very sophisticated pro-
grams can be found in this publication.
Other magazines for the Commodore 64 are available as well.
The best idea is to take a look at the various issues, including the
three recommended above, and see if there are any articles or pro-
293
grams on assembly/machine language programming. An even bet-
ter idea would be to write a letter to the editor of your favorite Com-
modore 64 magazine and tell them you want to see more listings and
articles on assembly language programming.
YOU'RE ON YOUR OWN
By this stage, you're well on your way to becoming a full-fledged
assembly language programmer. If you've gotten this far, you're
way ahead of where you were at the beginning. You should be able
to write routines of your own creation with the more common op-
codes and most addressing modes. What you do next is up to you.
If nothing else, I hope to have conveyed the fundamentals of
assembly language programming and given you enough confidence
to experiment on your own. If you can do that; then you'll be able
to go much further. Above all, assembly language programming
should be an exciting challenge. Put another way, it should be plain
fun. Before you know it, you'll wonder why anyone programs in
BASIC.
Now you can
FLY.
294
295
296
APPENDICES
APPENDIX A: KIDS' ASSEMBLER
APPENDIX B: 6510 OPCODES
APPENDIX C: MEMORY MAP 1: DIAGRAM
APPENDIX D: MEMORY MAP 2: PLACES TO VISIT
APPENDIX E: BASIC TOKEN CHART
APPENDIX F: HEXADECIMAL-DECIMAL CONVERSION
: . CHART
APPENDIX G: DECIMAL-HEXADECIMAL CONVERSION
CHART
APPENDIX H: SCREEN STORAGE ADDRESS TABLE
APPENDIX I: COLOR STORAGE LOCATION TABLE
APPENDIX J: ASCII CODE
APPENDIX K: SCREEN STORAGE DISPLAY CODES
297
298
APPENDIX A
KIDS' ASSEMBLER
This version of the Kids' Assembler has all opcodes for the 6510.
There are two ENDING ROUTINES, one for disk and one for
tape. The disk version is in the main listing between lines 740 and
960. If you are using a cassette, there is a second ENDING
ROUTINE at the end of the main listing.
10 POKE 53281,1 : POKE 53280,1 : PRINTCHR$(144)
20PRINTCHR$(147)
30 DIM DEC%(151),OPCODE$(151),BYTE%(151)
40 GOSUB 2510
50 FOR I = TO 150 : READ DEC%(I) : READ
OPCODE$(l) : READ BYTE%(I)
60 NEXT I
70 PRI NT CH R$(1 46);CH R$(1 47)
80 PRINT "ADRS";
TAB(10);"OPCODE";TAB(25);"OPERAND"
90 FOR X= 1 TO 40 : PRINT CHR$(114); : NEXT
100 PRINT
110) RFM ****************************
120 REM SET ADDRESS AND INPUT OPCODE
299
nin rfm ****************************
140 SA = : PRINT "PRESS {RETURN}
TO DEFAULT TO
49152"
150 INPUT'STARTING ADDR";SA : IF SA = ©
THEN SA = 491 52
16©BA = SA
17© PRINT SA;TAB(1©)180 INPUT OC$ : IF OC$ = "Q"
THEN 74©
190 C =
20© IF OC$ = OPCODE$(C) THEN D% = DEC%(C) :
B% = BYTE%(C) : GOTO 23©
210C = C+1 : IF C = 152 THEN PRINT
TAB(1©);CHR$(18);"ERROR";CHR$(146) : GOTO 17©
22© GOTO 2©©
230 IF B% = 1 THEN POKE SA,D%: SA = SA + 1 :
GOTO 170
94(71 RFM *************
250 REM ENTER OPERAND
ORC\ RFM *************
270 PRINT TAB(25);: PRINT CHR$(145);:INPUT OPR$
280 IF LEFT$(OPR$,1) <> "$" THEN
OPER = VAL(OPR$)
290 IF LEFT$(OPR$,1) = "$" THEN GOSUB 450
300 IF OPER > 65535 THEN GOSUB 590 : OPER = 0:
GOTO 27©
310IFOC$ = "BNE"OROC$ = "BEQ"
THEN GOSUB 660
320 IF OC$ = "BCC" OR OC$ = "BCS"
THEN GOSUB 660
330 IF OC$ = "BPL" OR OC$ = "BMI"
THEN GOSUB 660
34© IF 0C$ = "BVC" OR 0C$ = "BVS"
THEN GOSUB 660
350 IF BF= 1 THEN BF = © : GOTO 27©
36© IF OPER > 255 AND B% < 3 THEN GOSUB 520:
OPER = 0: GOTO 270
370 IF OPER > 255 THEN GOSUB 6©0
oqj» RFM ************
390 REM COMPILE CODE
300
400 REM ************
410 IF B% =2 THEN POKE SA,D% : SA = SA + 1
420 IF B% = 2 THEN POKE SA.OPER : SA = SA + 1 :
OPER = 0: GOTO 170
430 POKE SA,D%: SA = SA + 1
440 POKE SA,LB : SA = SA + 1 : POKE SA,HB :
SA = SA + 1 :OPER = 0: GOTO 170
450 REM **********************
460 REM CONVERT HEX TO DECIMAL
470 REM **********************
480H$=MID$(OPER$,2)
490 FOR L= 1 TO LEN(H$) : HD = ASC(MID$(H$,L,1))
500OPER = OPER*16+HD-48 + ((HD> 57)*7)
510 NEXT L : RETURN
520 REM **********
530 REM ERROR TRAP
540 REM **********
550 PRINT CHR$(18);"ERROR- MUST BE LESS
THAN 256"
560 FOR W = 1 TO 400 : NEXT W : PRINT CHR$(146); :
PRINT CHR$(145)570 FOR X= 1 TO 27 : PRINT
CHR$(32); : NEXT
580 PRINT CHR$(157);CHR$(157);CHR$(145) :RETURN
590 PRINT CHR$(18);"VALUE OVER 65535
($FFFF)";CHR$(146) : RETURN
600 REM ************************
610 REM CONVERT TO 2 BYTE NUMBER
620 REM ************************
630 LB = OPER - 1 NT(OPER/256)* 256
640HB = INT(OPER/256)
650 RETURN
fifi0 RFM *************
670 REM BRANCH OFFSET
680 REM *************
690IFSA> OPER AND SA - OPER > 128 THEN
PRINT "BRANCH TOO
FAR":BF=1:OPER = 0:RETURN
700IFSA> OPER AND OPER -SA > 127 THEN
PRINT "BRANCH TOO
FAR":BF = 1:OPER = 0:RETURN
301
710IFSA >OPERTHENOPER= 254 - (SA - OPER)
720IFSA < OPER THEN OPER = (OPER-SA)-2
730 RETURN
~7A0\ RFM **************
750 REM ENDING ROUTINE
7fiffl RFM **************
770 NB = SA-BA
780 PRINT CHR$(147)
790 FOR X= 1 TO 5 : PRINT : NEXT
800 INPUT'SAVE PROGRAM(Y/N)";AN$
810IFAN$ = "Y"THEN870
820 PRINT : PRINT : PRINT "PROGRAM
IS";NB;"BYTES LONG"
830 PRINT "TO EXECUTE 'SYS"';BA : PRINT
840 INPUT "(B)EGIN AGAIN OR (E)ND";DE$
850IFDE$ = "B"THEN70
860 PRINT : PRINT'END" : END
870 PRINT CHR$(147) : FOR X= 1 TO 5 : PRINT :
NEXT
880 LB = BA - INT(BA/256)*256 : HB = INT(BA/256)
890 INPUT "ENTER FILE NAME";NF$
:NF$ = "0:" + NF$ + STR$(BA) + ",P,W"
900 OPEN2,8,2,NF$
910 PRINT#2,CHR$(LB) + CHR$(HB)920 FOR X= BA
TOSA-1:OC=PEEK(X)
930 PRINT#2,CHR$(OC)940 NEXTX
950 CLOSE2
960 GOTO 820
Q7W RFM ***********
980 REM OPCODE DATA
990 REM ***********
1000 DATA 0,BRK,1
1010 DATA 1,(ORA-X),2
1020 DATA 5,ORA-Z,2
1030 DATA 6,ASL-Z,2
1040 DATA 8,PHP,1
1050 DATA 9,ORA#,2
1060 DATA 10,ASL-A,1
1070 DATA 13,ORA,3
1080 DATA 14,ASL,3
302
1090 DATA 16,BPL,2
1100 DATA 17,(0RA-Y),1
1110 DATA 21 ,0RA-ZX,2
1 1 20 DATA 22,ASL - ZX,2
1130 DATA 24,CLC,1
1140 DATA 25,ORA-Y,3
1150 DATA 29,ORA-X,3
1160 DATA 30,ASL-X,3
1170 DATA 32, JSR,3
1180 DATA 33,(AND-X),2
1190 DATA 36,BIT-Z,2
1200 DATA 37,AND-Z,2
1210 DATA 38,ROL-Z,2
1220 DATA 40,PLP,1
1230 DATA 41 ,AND#,2
1240 DATA 42,ROL-A,1
1250 DATA 44,BIT,3
1260 DATA 45,AND,3
1270 DATA 46,ROL,3
1280 DATA 48,BMI,2
1290 DATA 49,(AND- Y),2
1300 DATA 53,AND-ZX,2
1310 DATA 54,ROL-ZX,2
1320 DATA 56,SEC,1
1330 DATA 57,AND-Y,3
1340 DATA 61,AND-X,3
1350 DATA 62, R0L-X.3
1360 DATA 64,RTI,1
1370 DATA 65,(EOR-X),2
1380 DATA 69, E0R-Z,2
1390 DATA 70.LSR-Z.2
1400 DATA 72,PHA,1
1410 DATA 73,EOR#,2
1420 DATA 74,LSR-A,1
1430 DATA 76,JMP,3
1440 DATA 77,EOR,3
1450 DATA 78,LSR,3
1460 DATA 80,BVC,2
1470 DATA 81 ,(E0R-Y),2
1480 DATA 85.EOR - ZX,2
303
1490 DATA 86.LSR - ZX,2
1500 DATA 88,CLI,1
1510 DATA 89,EOR-Y,3
1520 DATA 93, EOR-X.3
1530 DATA 94,LSR-X,3
1540 DATA 96,RTS,1
1550 DATA 97,(ADC - X),2
1560 DATA 101, ADC -Z,2
1570 DATA 102,ROR-Z,2
1580 DATA 104.PLA.1
1590 DATA 105,ADC#,2
1600 DATA 1 06, ROR-A.1
1610 DATA 108,(JMP),3
1620 DATA 109,ADC,3
1630 DATA 110,ROR,3
1640 DATA 11 2,BVS,2
1650 DATA 113,(ADC-Y),2
1 660 DATA 1 1 7.ADC - ZX,2
1670 DATA 118,ROR-ZX,2
1680 DATA 120,SEI,1
1690 DATA 121, ADC -Y,3
1700 DATA 125, ADC -X,3
1710 DATA 126,ROR-X,3
1720 DATA 129,(STA-X),2
1730 DATA 132,STY-Z,2
1740 DATA 133,STA-Z,2
1750 DATA 134,STX-Z,2
1760 DATA 136,DEY,1
1770 DATA 138,TXA,1
1780 DATA 140,STY,3
1790 DATA 1 41 ,STA,3
1800 DATA 142,STX,3
1810 DATA 144,BCC,2
1820 DATA 145,(STA-Y),2
1830 DATA 148,STY-ZX,2
1840 DATA 149,STA-ZX,2
1850 DATA 150,STX-ZX,2
1860 DATA 152.TYA.1
1870 DATA 153.STA-Y.3
1880 DATA 154,TXS,1
304
1890 DATA 157,STA-X,3
1900 DATA 160,LDY#,2
1910 DATA 1 61 ,(LDA-X),2
1920 DATA 162,LDX#,2
1930 DATA 164,LDY-Z,2
1940 DATA 165.LDA-Z.2
1950 DATA 166,LDX-Z,2
1960 DATA 168,TAY,1
1970 DATA 169,LDA#,2
1980 DATA 170,TAX,1
1990 DATA 172,LDY,3
2000 DATA 173,LDA,3
2010 DATA 174,LDX,3
2020 DATA 176,BCS,2
2030 DATA 177,(LDA-Y),2
2040 DATA 180,LDY-ZX,2
2050 DATA 181,LDA-ZX,2
2060 DATA 182,LDX-ZY,2
2070 DATA 184,CLV,1
2080 DATA 185,LDA-Y,3
2090 DATA 186.TSX.1
2100 DATA 1 88, LDY-X,3
2110 DATA 1 89, LDA-X.3
2120 DATA 190,LDX-Y,3
2130 DATA 192,CPY#,2
2140 DATA 193,(CMP-X),2
2150 DATA 196,CPY-Z,2
2160 DATA 197,CMP-Z,2
2170 DATA 198,DEC-Z,2
2180 DATA 200,INY,1
2190 DATA 201,CMP#,2
2200 DATA 202,DEX,1
2210 DATA 204.CPY.3
2220 DATA 205,CMP,3
2230 DATA 206,DEC,3
2240 DATA 208,BNE,2
2250 DATA 209,(CMP- Y),2
2260 DATA 213,CMP-ZX,2
2270 DATA 214,DEC-ZX,2
2280 DATA 216,CLD,1
305
2290 DATA 217.CMP- Y,3
2300 DATA 221 ,CMP-X,3
2310 DATA 222,DEC-X,3
2320 DATA 224,CPX#,2
2330 DATA 225,(SBC - X),2
2340 DATA 228,CPX-Z,2
2350 DATA 229.SBC - Z,2
2360 DATA 230,INC-Z,2
2370 DATA 232,INX,1
2380 DATA 233,SBC#,2
2390 DATA 234.NOP.1
2400 DATA 236,CPX,3
2410 DATA 237,SBC,3
2420 DATA 238,INC,3
2430 DATA 240.BEQ.2
2440 DATA 241 ,(SBC - Y),2
2450 DATA 245,SBC - ZX,2
2460 DATA 246,INC-ZX,2
2470 DATA 248.SED.1
2480 DATA 249.SBC- Y,3
2490 DATA 253.SBC - X,3
2500 DATA 254,1 NC - X,3
2510 REM ******
2520 REM HEADER
2530 REM ******
2540 CR$ = "(C) COPYRIGHT 1984" : NM$= "BY
WILLIAM B. SANDERS"
2550 BK$ = "ASSEMBLY LANGUAGE FOR KIDS:"
:CM$ = "COMMODORE 64"
2560 IS$ = "SEE" : F$ = "FOR DOCUMENTATION"
2570 H = 20 - LEN(CR$)/2 : PRINT TAB(H);CR$
2580 H = 20 - LEN(NM$)/2 : PRINT TAB(H);NM$
2590 PRINT: H = 20 - LEN(IS$)/2 : PRINT TAB(H);IS$
PRINT
2600 H = 20 - LEN(BK$)/2 : PRINT TAB(H);BK$
:H = 20 - LEN(CM$)/2: PRINT TAB(H);CM$
2610 H = 20 - LEN(NM$)/2 : PRINT TAB(H);NM$ :
PRINT
2620 H = 20 - LEN(F$)/2 : PRINT TAB(H);F$
2630 LD$ = "LOADING ARRAY" : FOR X = 1 TO 10 :
306
PRINT : NEXT : H = 20 - LEN(LD$)/2
2640 PRINT TAB(H);CHR$(18);LD$
2650 RETURN
CASSETTE ENDING ROUTINE
740 REM **************
750 REM ENDING ROUTINE
760 REM **************
770 NB = SA-BA
780 PRINT CHR$(147)
790 FOR X= 1 TO 5 : PRINT : NEXT
800 INPUP'SAVE PROGRAM(Y/N)";AN$
810 IF AN$ = "Y" THEN 870
820 PRINT : PRINT : PRINT "PROGRAM
IS";NB;"BYTES LONG"
830 PRINT "TO EXECUTE 'SYS"';BA : PRINT
840 INPUT "(B)EGIN AGAIN OR (E)ND";DE$
850IFDE$ = "B"THEN70
860 PRINT : PRINP'END" : END
870 PRINT CHR$(147) : FOR X= 1 TO 5 : PRINT
NEXT
880 REM *** TAPE SAVE ***
890 INPUT "ENTER FILE NAME";NF$
900OPEN21,1,1,NF$
910 PRINT#21,BA
920 FOR X = BA TO SA - 1 : OC = PEEK(X)
930 PRINT#21,OC
940 N EXT X
950 CLOSE21
960 GOTO 820
CASSETTE PROGRAM LOADER
Since you cannot load from tape the same as you can from disk, a
special loader program is required. Your assembled program is sav-
ed as a SEQ file, and it must be read into memory and then POKEd
into memory. The following program will do that for you and show
you where it is loaded on the screen:
307
10 PRINT CHR$(147) : X =
20 INPUT "NAME OF FILE ";NF$
30OPEN21,1,0,NF$
40 INPUT#21,BA
50 INPUT#21,OC
60POKEBA + X,OC
70 PRINT BA + X.OC
80X = X + 1
90IFST = 0THEN50
100 CLOSE 21
308
APPENDIX B
6510 OPCODES
Machine
Mnemonic Addressing
Opcodes
Opcodes
Mode
Dec
Hex
109
$6D
ADC
Absolute
125
$7D
ADC
Absolute.X
121
$79
ADC
Absolute,Y
105
$69
ADC
Immediate
097
$61
ADC
(lndirect,X)
113
$71
ADC
(lndirect),Y
101
$65
ADC
Zero Page
117
$75
ADC
Zero Page,X
045
$2D
AND
Absolute
061
$3D
AND
Absolute,X
057
$39
AND
Absolute,Y
041
$29
AND
Immediate
033
$21
AND
(lndirect,X)
049
$31
AND
(lndirect),Y
037
$25
AND
Zero Page
053
$35
AND
Zero Page,X
309
Machine
Mnemonic Addressing
Opcodes
Opcodes
Mode
Dec
Hex
014
$0E
ASL
Absolute
030
$1E
ASL
Absolute.X
010
$0A
ASL
Accumulator
006
$06
ASL
Zero Page
022
$16
ASL
Zero Page,X
144
$90
BCC
Relative
176
$B0
BCS
Relative
240
$F0
BEQ
Relative
044
$2C
BIT
Absolute
036
$24
BIT
Zero Page
048
$30
BMI
Relative
208
$D0
BNE
Relative
016
$10
BPL
Relative
000
$00
BRK
Implied
080
$50
BVC
Relative
112
$70
BVS
Relative
024
$18
CLC
Implied
216
$D8
CLD
Implied
088
$58
CLI
Implied
184
$B8
CLV
Implied
205
$CD
CMP
Absolute
221
$DD
CMP
Absolute.X
217
$D9
CMP
Absolute, Y
201
$C9
CMP
Immediate
193
$C1
CMP
(IndirectX)
209
$D1
CMP
(lndirect),Y
197
$C5
CMP
Zero Page
213
$D5
CMP
Zero Page,X
236
$EC
CPX
Absolute
224
$E0
CPX
Immediate
228
$E4
CPX
Zero Page
204
$CC
CPY
Absolute
192
$C0
CPY
Immediate
196
$C4
CPY
Zero Page
206
$CE
DEC
Absolute
222
$DE
DEC
Absolute.X
310
Machine
Mnemonic Addressing
Opcodes
Opcodes
Mode
Dec
Hex
198
$C6
DEC
Zero Page
214
$D6
DEC
Zero Page,X
202
$CA
DEX
Implied
136
$88
DEY
Implied
077
$4D
EOR
Absolute
093
$5D
EOR
Absolute,X
089
$59
EOR
Absolute,Y
073
$49
EOR
Immediate
065
$41
EOR
(IndirectX)
081
$51
EOR
(lndirect),Y
069
$45
EOR
Zero Page
085
$55
EOR
Zero Page,X
238
$EE
INC
Absolute
254
$FE
INC
Absolute,X
230
$E6
INC
Zero Page
246
$F6
INC
Zero Page
232
$E8
I NX
Implied
200
$C8
I NY
Implied
076
$4C
JMP
Absolute
108
$6C
JMP
(Indirect)
032
$20
JSR
Absolute
173
$AD
LDA
Absolute
189
$BD
LDA
Absolute,X
185
$B9
LDA
Absolute,Y
169
$A9
LDA
Immediate
161
$A1
LDA
(lndirect,X)
177
$B1
LDA
(lndirect),Y
165
$A5
LDA
Zero Page
181
$B5
LDA
Zero Page,X
174
$AE
LDX
Absolute
190
$BE
LDX
Absolute,Y
162
$A2
LDX
Immediate
166
$A6
LDX
Zero Page
182
$B6
LDX
Zero Page,Y
172
$AC
LDY
Absolute
311
Machine
Mnemonic Addressing
Opcodes
Opcodes
Mode
Dec
Hex
188
SBC
LDY
Absolute.X
160
$A0
LDY
Immediate
164
$A4
LDY
Zero Page
180
$B4
LDY
Zero Page,X
078
$4E
LSR
Absolute
094
$5E
LSR
Absolute.X
074
$4A
LSR
Accumulator
070
$46
LSR
Zero Page
086
$56
LSR
Zero Page.X
234
$EA
NOP
Implied
013
$0D
ORA
Absolute
029
$1D
ORA
Absolute.X
025
$19
ORA
Absolute, Y
009
$09
ORA
Immediate
001
$01
ORA
(lndirect,X)
017
$11
ORA
(lndirect),Y
005
$05
ORA
Zero Page
021
$15
ORA
Zero Page,X
072
$48
PHA
Implied
008
$08
PHP
Implied
104
$68
PLA
Implied
040
$28
PLP
Implied
046
$2E
ROL
Absolute
062
$3E
ROL
Absolute.X
042
$2A
ROL
Accumulator
038
$26
ROL
Zero Page
054
$36
ROL
Zero Page.X
110
$6E
ROR
Absolute
126
$7E
ROR
Absolute.X
106
$6A
ROR
Accumulator
102
$66
ROR
Zero Page
118
$76
ROR
Zero Page.X
064
$40
RTI
Implied
096
$60
RTS
Implied
237
$ED
SBC
Absolute
253
$FD
SBC
Absolute.X
312
Machine
Mnemonic Addressing
Opcodes
Opcodes
Mode
Dec
Hex
249
$F9
SBC
Absolute.Y
233
$E9
SBC
Immediate
225
$E1
SBC
(lndirect,X)
241
$F1
SBC
(Indirectj.Y
229
$E5
SBC
Zero Page
245
$F5
SBC
Zero Page,X
056
$38
SEC
Implied
248
$F8
SED
Implied
120
$78
SEI
Implied
141
$8D
STA
Absolute
157
$9D
STA
Absolute,X
153
$99
STA
Absolute, Y
129
$81
STA
(lndirect,X)
145
$91
STA
(Indirect).Y
133
$85
STA
Zero Page
149
$95
STA
Zero Page,X
142
$8E
STX
Absolute
134
$86
STX
Zero Page
150
$96
STX
Zero Page.Y
140
$8C
STY
Absolute
132
$84
STY
Zero Page
148
$94
STY
Zero Page,X
170
$AA
TAX
Implied
168
$A8
TAY
Implied
186
$BA
TSX
Implied
138
$8A
TXA
Implied
154
$9A
TXS
Implied
152
$98
TYA
Implied
313
314
APPENDIX C
Memory Map 1 : Diagram
$E000-$FFFF
57344-65535
8K Kernal ROM
$D000-$DFFF
53248-57343
4K I/O or
Character ROM
$C000-$CFFF
49152-53247
4KRAM
$A000-$BFFF
40960-49151
BASIC ROM
or ROM Plug-in
$8000-$9FFF
3276840959
8KRAM
or ROM Plug-in
315
$4000-$7FFF 16K RAM
16384-32767
$0000-$3FFF 16K RAM
00000-16383
$0800 BASIC begins
2048
316
APPENDIX D
MEMORY MAP 2 : Places to Visit
This map has been abridged so that you can quickly look up the
most-often used subroutines, pointers and free spaces you will be
using in assembly language programs. All addresses are given in
hexadecimal. Since there are fewer digits in hexadecimal numbers,
these values are easier to memorize. (You gotta do it sooner or
later.)
Address
What's There
$©-$2A
Misc. Flags and Pointers
$2B-2C
Pointer to beginning of BASIC
$2D-$2E
Pointer to start of variables / end of
BASIC program
$2F-$C4
Misc. BASIC flags, functions and
pointers
$C5
ASCII of last key pressed
$C6
Number of characters in keyboard
buffer
$C7
Screen reverse: = off 18 = on
317
Address
What's There
$C8-$FA
Misc. flags, functions and pointers
$FB-$FE
Free zero page addresses
$FF-$280
Misc. flags, functions and pointers
$281-$282
Beginning of BASIC memory
$283-$284
Top of memory
$285-$2BF
Misc. flags, functions and pointers
$2C0-$2FF
Block 11 sprite area
$300-$333
Misc. flags, functions and pointers
$334-$33B
Free space
$33C-$3FB
Cassette buffer- MACHINE
LANGUAGE STORAGE
$3FC-$3FF
Free space
$340-$37F
Block 13 sprite area
$380-3BF
Block 14 sprite area
$3C0-$3FF
Block 15 sprite area
$400-$7FF
Screen memory 24 x 40
$7F8-$7FF
Sprite pointers for data
$800-$9FFF
BASIC RAM
$8000-$9FFF
Plug in ROM or MACHINE LANGUGE
STORAGE
$A000-$BFFF
BASIC ROM
$C000-CFFF
Free RAM - MACHINE LANGUAGE
STORAGE
$D000
Sprite X position
$D001
Sprite Y position
$D002
Sprite 1 X position
$D003
Sprite 1 Y position
$D004
Sprite 2 X position
$D005
Sprite 2 Y position
$D006
Sprite 3 X position
$D007
Sprite 3 Y position
$D008
Sprite 4 X position
$D009
Sprite 4 Y position
$D00A
Sprite 5 X position
$D00B
Sprite 5 Y position
$D00C
Sprite 6 X position
$D00D
Sprite 6 Y position
$D00E
Sprite 7 X position
318
Address
What's There
$D00F
Sprite 7 Y position
$D010
High byte of sprite X position
$D011-$D01F
Misc. flags, functions and pointers
$D020
Border color register
$D021
Background color register
$D022
Background color 1 register
$D023
, Background color 2 register
$D024
Background color 3 register
$D025-$D026
Sprite muiti-color registers
$D027-$D02E
Sprite color registers
$D400-$D418
Sound registers
$D419
Game paddle 1 or 3
$D41A
Game paddle 2 or 4
$D41B
Random number generator
$D41C-$DD0F Mis. registers
$E000-$FFFF
Kernal ROM
$E544
Clear screen
$E566
Home cursor in upper left hand corner
$E716
Output to screen
$E8E7
Scroll screen
$FF9F
SCNKEY - scan keyboard
$FFCF
CHRIN - input a character
$FFD2
CHROUT - output a character
$FFE4
GETIN - get a character
$FFF0
PLOT - read or set X,Y postion of
cursor
319
320
APPENDIX E
BASIC TOKEN CHART
Your Commodore 64 reads BASIC statements as tokenized
codes. In a disassembled listing of a BASIC program, the following
tokenized values can be found representing the BASIC statements.
DEC HEX Keybd.
DEC HEX Keybd.
128 $80 -END
165 $A5 - FN
129 $81 - FOR
166 $A6 - SPC(
130 $82 - NEXT
167 $A7- THEN
131 $83 - DATA
168 $A8 - NOT
132$84-INPUT#
169 $A9 - STEP
133 $85 - INPUT
170 $AA - +
134 $86 -DIM
171 $AB--
135 $87 - READ
172 $AC-*
136 $88 - LET
173 $AD-/
137 $89 - GOTO
174 $AE-
138$8A-RUN
175 $AF - AND
139 $8B - IF
176$B0-OR
140 $8C - RESTORE
177 $B1 -
141 $8D - GOSUB
178 $B2- =
321
DEC HEX Keybd.
DEC HEX Keybd.
142 $8E - RETURN
179 $B3-
143$8F-REM
180 $B4 - SGN
144 $90 - STOP
181 $B5 - INT
145 $91 - ON
182 $B6 - ABS
146 $92 - WAIT
183 $B7 - USR
147 $93 - LOAD
184 $B8 - FRE
148 $94 - SAVE
185 $B9 - POS
149 $95 - VERIFY
186 $BA - SQR
150 $96 - DEF
187$BB-RND
151 $97 - POKE
188$BC-LOG
152 $98 - PRINT*
189 $BD - EXP
153 $99 -PRINT
190$BE-COS
154 $9A - CONT
191$BF-SIN
155 $9B - LIST
192 $C0 - TAN
156 $9C - CLR
193 $C1 - ATN
157 $9D - CMD
194 $C2- PEEK
158 $9E - SYS
195 $C3 - LEN
159 $9F - OPEN
196$C4-STR$
160 $A0 - CLOSE
197 $C5 - VAL
161 $A1 - GET
198$C6-ASC
162 $A2 - NEW
199 $C7 - CHR$
163 $A3 - TAB(
200 $C8 - LEFT$
164 $A4 - TO
201$C9-RIGHT$
202 $CA - MID$
322
APPENDIX F
HEXADECIMAL-DECIMAL
CONVERSION
HEX DEC HEX DEC HEX DEC HEX DEC HEX DEC
51 $66 = 102 $99 = 153 $CC = 204
52 $67 = 103 $9A = 154 $CD = 205
53 $68 = 104 $9B = 155 $CE = 206
54 $69 = 105 $9C = 156 $CF = 207
55 $6A = 106 $9D=157 $D0 = 208
56 $6B = 107 $9E=158 $D1=209
57 $6C=108 $9F=159 $D2 = 210
58 $6D = 109 $A0 = 160 $D3 = 21 1
59 $6E=110 $A1=161 $D4 = 212
60 $6F = 111 $A2 = 162 $D5 = 213
61 $70 = 112 $A3 = 163 $D6 = 214
62 $71 = 113 $A4=164 $D7 = 215,
63 $72 = 114 $A5=165 $D8 = 216
64 $73=115 $A6=166 $D9 = 217
65 $74 = 116 $A7=167 $DA = 218
66 $75=117 $A8=168 $DB = 219
67 $76 = 118 $A9 = 169 $DC = 220
68 $77 = 1 19 $AA = 1 70 $DD = 221
323
$00 =
$33 =
$01 =
1 $34 =
$02 =
2 $35 =
$03 =
3 $36 =
$04 =
4 $37 =
$05 =
5 $38 =
$06 =
6 $39 =
$07 =
7 $3A =
$08 =
8 $3B =
$09 =
9 $3C =
$0A =
10 $3D =
$0B =
11 $3E =
$0C =
12 $3F =
$0D =
13 $40 =
$0E =
14 $41 =
$0F =
15 $42 =
$10 =
16 $43 =
$11 =
17 $44 =
HEX DEC HEX DEC HEX DEC HEX DEC HEX DEC
$12 =
$13 =
$14 =
$15 =
$16 =
$17 =
$18 =
$19 =
$1A =
$1B =
$1C =
$1D =
$1E =
$1F =
$20 =
$21 =
$22 =
$23 =
$24 =
$25 =
$26 =
$27 =
$28 =
$29 =
$2A =
$2B =
$2C =
$2D =
$2E =
$2F =
$30 =
$31 =
$32 =
$33 =
18 $45 =
19 $46 =
20 $47 =
21 $48 =
22 $49 =
23 $4A =
24 $4B =
25 $4C =
26 $4D =
27 $4E =
28 $4F =
29 $50 =
30 $51 =
31 $52 =
32 $53 =
33 $54 =
34 $55 =
35 $56 =
36 $57 =
37 $58 =
38 $59 =
39 $5A =
40 $5B =
41 $5C =
42 $5D =
43 $5E =
44 $5F =
45 $60 =
46 $61 =
47 $62 =
48 $63 =
49 $64 =
50 $65 =
51 $66 =
69 $78=
70 $79=
71 $7A:
72 $7B:
73 $7C:
74 $7D:
75 $7E=
76 $7F=
77 $80=
78 $81 =
79 $82=
80 $83=
81 $84=
82 $85=
83 $86=
84 $87=
85 $88=
86 $89=
87 $8A=
88 $8B:
89 $8C:
90 $8D:
91 $8E:
92 $8F:
93 $90:
94 $91=
95 $92:
96 $93=
97 $94:
98 $95=
99 $96=
100 $97:
101 $98:
102 $99:
120 $AB:
121 $AC:
:122 $AD:
:123 $AE:
:124 $AF:
:125 $B0=
: 126 $B1 =
=127 $B2=
128 $B3=
129 $B4=
130 $B5=
131 $B6:
132 $B7=
133 $B8=
134 $B9=
135 $BA =
136 $BB:
137 $BC:
:138 $BD:
:139 $BE:
:140 $BF:
= 141 $C0 =
:142 $C1:
=143 $C2:
=144 $C3:
:145 $C4:
:146 $C5:
:147 $C6:
:148 $C7:
:149 $C8=
:150 $C9 =
:151 $CA:
:152 $CB:
:153 $CC:
171 $DE = 222
172$DF = 223
173 $E0 = 224
174 $E1=225
175 $E2 = 226
176 $E3 = 227
177 $E4 = 228
178 $E5 = 229
179 $E6 = 230
180 $E7 = 231
181 $E8 = 232
182 $E9 = 233
183 $EA = 234
184 $EB = 235
185 $EC = 236
:186$ED = 237
= 187 $EE = 238
= 188 $EF = 239
= 189 $F0 = 240
= 190 $F1=241
= 191 $F2 = 242
:192 $F3 = 243
193 $F4 = 244
194 $F5 = 245
195 $F6 = 246
196 $F7 = 247
197 $F8 = 248
:198 $F9 = 249
;199 $FA = 250
200 $FB = 251
201 $FC = 252
= 202$FD = 253
:203$FE = 254
:204$FF = 255
324
APPENDIX G
DECIMAL-HEXADECIMAL
CONVERSION CHART
DEC HEX DEC HEX DEC HEX DEC HEX DEC HEX
= $33 102 = $66 153 = $99 204 = $CC
= $34 103 = $67 154 = $9A 2C5 = $CD
= $35 104 = $68 155 = $9B 206 = $CE
= $36 105 = $69 156 = $9C 207 = $CF
= $37 106 = $6A 157 = $9D 208 = $D0
= $38 107 = $6B 158 = $9E 209 = $D1
= $39 108 = $6C 159 = $9F 210 = $D2
= $3A 1 09 = $6D 160 = $A0 21 1 = $D3
= $3B 110 = $6E 161=$A1 212 = $D4
= $3C 111=$6F 162 = $A2 213 = $D5
= $3D 112 = $70 163 = $A3 214 = $D6
= $3E 113 = $71 164 = $A4 215 = $D7
= $3F 114 = $72 165 = $A5 216 = $D8
= $40 115 = $73 166 = $A6 217 = $D9
= $41 116 = $74 167 = $A7 218 = $DA
= $42 117 = $75 168 = $A8 219 = $DB
= $43 118 = $76 169 = $A9 220 = $DC
= $44 1 19 = $77 170 = $AA 221 = $DD
325
= $00 51
1
= $01 52
2
= $02 53
3
= $03 54
4
= $04 55
5
= $05 56
6
= $06 57
7
= $07 58
8
= $08 59
9
= $09 60
10
= $0A 61
11
= $0B 62
12
= $0C 63
13
= $0D 64
14
= $0E 65
15
= $0F 66
16
= $10 67
17
= $11 68
DEC HEX DEC HEX DEC HEX DEC HEX DEC HEX
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
:$12 69 =
:$13 70 :
:$14 71 =
:$15 72 =
:$16 73 :
:$17 74 =
:$18 75 =
:$19 76 =
$1A 77 =
$1B 78 =
$1C 79 =
$1D 80 :
:$1E 81 :
:$1F 82 :
:$20 83 :
:$21 84 :
= $22 85 :
:$23 86 :
:$24 87 :
:$25 88 :
:$26 89 :
:$27 90 :
=$28 91 :
= $29 92 -.
:$2A 93 =
:$2B 94 :
:$2C 95 :
:$2D 96 :
:$2E 97 :
:$2F 98 :
:$30 99 :
=$31 100:
:$32 101:
:$33 102:
:$45 120:
:$46 121:
=$47 122=
:$48 123=
:$49 124=
$4A 125=
$4B 126:
$4C 127=
$4D 128:
:$4E 129:
:$4F 130:
:$50 131=
:$51 132:
:$52 133:
:$53 134:
=$54 135:
:$55 136:
:$56 137:
:$57 138:
=$58 139=
:$59 140:
:$5A 141:
:$5B 142:
:$5C 143:
:$5D 144:
:$5E 145:
:$5F 146:
=$60 147:
:$61 148:
:$62 149:
:$63 150:
:$64 151:
=$65 152:
:$66 153:
:$78 171
=$79 172
$7A 173
$7B 174
$7C 175
$7D 176
:$7E 177
:$7F 178:
:$80 179:
=$81 180:
:$82 181:
:$83 182:
:$84 183:
:$85 184:
=$86 185:
:$87 186=
:$88 187=
:$89 188:
:$8A 189:
:$8B 190:
:$8C 191:
:$8D 192:
:$8E 193
:$8F 194:
:$90 195:
:$91 196:
:$92 197:
:$93 198:
=$94 199=
:$95 200:
:$96 201:
:$97 202:
:$98 203:
:$99 204:
= $AB222 = $DE
= $AC223 = $DF
= $AD 224 = $E0
= $AE 225 = $E1
= $AF 226 = $E2
= $B0 227 = $E3
= $B1 228 = $E4
= $B2 229 = $E5
= $B3 230 = $E6
$B4 231=$E7
$B5 232 = $E8
$B6 233 = $E9
$B7 234 = $EA
$B8 235 = $EB
$B9 236 = $EC
= $BA237 = $ED
= $BB 238 = $EE
= $BC 239 = $EF
= $BD 240 = $F0
= $BE 241=$F1
= $BF 242 = $F2
= $C0 243 = $F3
= $C1 244 = $F4
= $C2 245 = $F5
= $C3 246 = $F6
$C4 247 = $F7
$C5 248 = $F8
$C6 249 = $F9
$C7 250 = $FA
$C8 251=$FB
$C9 252 = $FC
$CA253 = $FD
$CB 254 = $FE
$CC255 = $FF
326
APPENDIX H
SCREEN STORAGE
ADDRESS TABLE
$400
$428
$450
$478
$4A0
$4C8
$4F0
$518
$540
$568
$598
$5B8
$5E0
$608
$830
$6A8
$6D0
$6F8
$720
$748
$770
$798
$7C0
1fl?4
1084
1104
1144
1184
1224
1264
1304
1344
1384
1424
1464
1504
1544
1584
1824
1864
1704
1744
1824
1904
1944
327
328
APPENDIX I
COLOR STORAGE LOCATION TABLE
$D8W 55296
$D828 55336
$D878 55416
$D8A0 55456
$D8C8 55496
$D8FB 55536
$D918 55576
$D94D 55616
$0968 55656
$D990 55696
$D9B8 55736
$D9Efl 55776
$DA08 55816
$DA3B 55856
$DA58 55896
$DA8fi 55936
$DAA8 55976
$DAM 561116
$DAF8 56856
$DB20 56696
$DB48 56136
$DB70 56176
$DB98 56216
329
330
APPENDIX J
ASCH CODE
These characters appear on the screen when sent from the ac-
cumulator using the CHROUT subroutine or output to screen
routines (JSR $E716). See APPENDIX K for screen codes that ap-
pear when the code is output with STA to a screen address
(1024-2023).
All values to the left are the ASCII values, and all characters,
symbols and descriptions to the right are screen displays
513
IKQ
153 Lt Green
204 □
1
524
l®3
154 Lt Blue
205 S
2
535
I04Q
155 Gray 3
206
3
546
I05
156 Purple
207 □
4
557
06 □
157 CRSR left 208 []
5 White
568
107
158 Yellow
209 H
6
57 9
I08D
159 Cyan
210 □
7
58:
89 S
16© SPACE
211 H
8 Sh-CMD off59 ;
10
161 E
212 D
9Sh-CMDon60<
11 n
162 y
213 Q
1®
61 =
12 □
163 n
214 13
11
62 >
113 ■
164 G
215 O
331
12 63?
13 RETURN 64 @
14 Lowercase 65 A
15 66 B
6 67 C
17 CRSR 68 D
down
18 RVS on 69 E
19 Home 7© F
CRSR
20 Delete 71 G
21 72 H
22 73 1
23 74 J
24 75 K
25 76 L
26 77 M
27 78 N
28 Red 79 O
29 CRSR right 80 P
30 Green
31 Blue
32 SPACE
33!
34"
35#
36$
37%
38&
39'
40(
41)
42*
43 +
44,
45-
46.
47/
480
491
502
81 Q
82 R
83S
84T
85U
86V
87 W
88X
89 Y
90Z
91 [
92 £
93 ]
94 t
95*-
96B
97
98D3
99H
10© B
101 □
14 □
15
16 D
17 Q
18 IE
19 O
20 B
21 a
22 H
23ffl
24 SD
25 m
26 H
27 s
28
29 Orange
30
31
32
33 f1
34 f3
3515
36 f7
37 f 2
38 f4
39 16
40 f8
165 G
166 B
167 □
168 Q
169 B
170 [J
171 E
172 a
173 H
174 ED
175 U
176 El
177 H
178 H
179 m
180 D
181 C
182 Of
183 n
184 H
185 U
186 □
187 H
188 [3
189 a
190 E
191 H
192 EB
41 Sh-
RETURN
42 Uppercase193 B
43 194 [JJ
44 Black 195 g
45 CRSR up 196 F=]
46 RVS off 197 Q
47CLR/ 198 □
HOME
48 INST 199 □
49 Brown 200 Q]
50LtRed 201 □
51 Gray 1 202 Q
52 Gray 2 203 □
216 B
217 O
218 B
219 EB
220 B
221 LH
222
223 H
224 SPACE
225
L
226
y
227
n
228
D
229
n
230
B
231
a
232
a
233
B
234
a
235
m
236
a
237
H
238
H
239
U
240
H
241
H
242
H
243
Bl
244
□
245
C
246
a
247
n
248
n
249
Q
250 Q
251 £
252 [J
253 E
254 E
255 [£
332
APPENDIX K
SCREEN STORAGE DISPLAY CODES
When values are stored in addresses 1024-2023 ($400-$7E7) they
will appear as the characters in this chart. The first set is upper case
and full graphics (UC), and the second set (UC/LQ is upper case
and lower case. If your keyboard is set to upper case and full
graphics, you will get the characters in the first set, and if it is set to
upper/lower case, you will get the second. Thus, any value that you
STA (or STX or STY) in these addresses will show up on the screen
as alphanumeric or graphic characters
# UC UC/LC .# UC UC/LC # UC UC/LC # UC UC/LC # UC UC/LC
@
@
513
3
102 B HS 153
204
1 A
a
52 4
4
103 □
3 154
205
2B
b
53 5
5
104 a
$4 155
206
3C
c
54 6
6
105 B.
a 156
207
4D
d
55 7
7
106 CI
3 157
208
5E
e
568
8
107 m
B 158
209
6F
f
57 9
9
io8 a
1 159
210
7G
g
58:
:
109 E
3 160
211
8H
h
59;
i
no ED
3 161
212
91
i
60 <
<
m U
J 162
213
10 J
J
61 =
=
n2 ca
-d 163
214
333
# UC UC/LC # UC UC/LC # UC UC/LC # UC UC/LC # UC UC/LC
11
12
K
L
13 M
14 N
15
16
O
P
17 Q
18 R
19
20
21
22
S
T
U
V
23 W
24 X
Y
Z
k
I
m
n
o
P
q
r
s
t
u
v
w
X
y
z
25
26
27
28
29
30
31
32 SPACE
33 ! !
34 " "
35 # #
36 $ $
37 % %
38 & &
40
41
42 *
43 4
44 ,
45 -
46 .
47 /
48
49 1
50 2
( (
) )
62 ► ►
63 ? ?
64 B B
65 H A
66 [D B
67 B
68 H
69 n
70 □
1
2
81
82 □
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
71
D
G
72
[1
H
73
□
1
74
U
J
75
W
K
76
D
L
77
K
M
78
N
79
□
80
□
P
13 HH
14 sa
15 BIB]
16 CD
17 EC
isoia
19 nc
!2o n r
121 Bt-
122 QE
23. .
i24H a
i25H a
I26E E
27 SB
28 - 255 reverse video of 0-127
Q
R
H s
D T
Q u
(El v
D w
S x
GO y
H z
m m
B H
E E
y h
n n
D D
□ □
334
INDEX
This INDEX covers the major references to various key terms.
Many of the opcodes were used throughout the book in several pro-
grams, but their only reference in the Index are to those places
where pertinent information was introduced or elaborated. Terms
in the Appendices are not in the Index but are located at the beginn-
ing of the Appendices.
A-B
ABSOLUTE MODE 132-133, 184
ACCUMULATOR 104-105
ADC 186-189
ADDRESSING MODES 123
ANIMATION 230-237
APPEND 151-153, 280-290
ASC 21 1-215
ASCII 200, 237
ASSEMBLE SOURCE CODE 56
ASSEMBLERS 5, 21-26, 24
ASSEMBLER64 80-82
ASSEMBLY LANGUAGE 6-8
BASIC 10-15
BASIC LOGIC 167
BEQ 170, 177-181
BINARY NUMBERS 91-101
BNE 170, 177-181
BOOKS 291-293
BRANCH 166-167, 171, 177-181, 179
BREAK FLAG 107
.BYTE (directive) 21 1-215, 262, 269
BYTE 123-124
C-D
CARRY FLAG 108
CHANGE 63, 79, 285
CHROUT 127, 195-197, 199,
225-227, 238
CLC 187-189,226
CMP 169
335
COLOR 200-201, 222
COLOR CODES 19
COMMODORE 64 MACRO
ASSEMBLER 73-89
COPY 64-65
CPX 169
CPY 169
DEC 184
DECIMAL FLAG 107
DELETE 61 80
DEX 148-150
DEY 148-150
DFB 262, 264-265, 269
DISASSEMBLER 26
DOS WEDGE64 73,78
E-F
EDITORS 23-24
EDITOR64 73-80, 280
EOR 205-207
FIELDS 27-28
FIND 80
FLAGS 105-108
FORMAT 76, 89
G-H
GET 79
GETIN 127, 194-195
GRAPHICS 219-246
GROTKE-GUY 37, 138
HEADER 77
HEXADECIMAL NUMBERS
91-101
HIGH BYTE 35, 97, 121-123
HIGH NIBBLE 99
l-J
IMMEDIATE MODE 132-133
IMPLIED MODE 132-133
INC 184-186
INDEXED ABSOLUTE MODE
154-156
INDEXED INDIRECT MODE
157-160
INDEXED MODE 172-177, 185
INDIRECT INDEXED MODE
161-163
INSERT 60, 285
INTERRUPT FLAG 107
INX 148-150
INY 148-150
JMP 177-181
JOYSTICK 193, 203-211, 237-246, 285
JSR 126-128, 198-199
K-L
KERNAL 127
KEYBOARD 194-203
KIDS* ASSEMBLER 29-51
LABEL FIELD 27
LABELS 195
LDA 129-132
LINE NUMBERS 113-116
LIST 59
LOAD PROGRAM 36-38, 67, 79,
138-139
LOADERS 82-83
LOADERS AND SAVERS 25
LOADING PROGRAMS 48-50
LOOPS 166, 168-177, 178
LOW BYTE 35, 97, 121-123
LOW NIBBLE 99
LOW RESOLUTION GRAPHICS
220-228
M-N
MACHINE LANGUAGE 6-8
MAGAZINES 293-294
MEMORY 116-119, 126, 134-142
MERGING SUBROUTINES 280-290
MERLIN64 ADD MODE 56
MERLIN64 COMMAND MODE 55
MERLIN64 EDITOR 55, 59-66
MERLIN64 EXECUTIVE MODE 54
MERLIN64 53-72
MESSAGE MAKER 215-217
MNEMONIC 5
MONITOR 25-26, 70, 83-88, 119-121
MOVE 65, 285
NEGATIVE FLAG 106
NESTED LOOPS 175-177
NUMBER CONVERSION 91-101
O-P
OBJECT CODE 24
336
OPCODES 5, 22, 23, 30-31, 39-41,
47-48, 123-124, 289-290
OPCODE FIELD 44
OPERANDS 22, 23, 32
OPERAND FIELD 44
ORG 125-126
OVERFLOW FLAG 107
PEEK 11-12, 219, 247
PLOT 127, 225-229, 232, 237
POKE 16-17, 219, 247
PRG FILE 38, 37
PRINTER 62-63
PROCESSOR STATUS REGISTER
105-106
PROGRAM COUNTER 110
PUT 78
R-S
RAM 44
REGISTERS 103-111
RENUMBER 76-77
REPLACE 65-66
SAVE CODE 78
SAVE FILE 57, 86
SAVE GRAPHICS 228-229
SAVE OBJECT CODE 58-59
SAVE SOURCE CODE 57
SBC 186, 190-191
SCNKEY 127, 194-195
SCREEN ADDRESSES 140-141
SEC 187, 190-191
SEQ FILE 36
SEQUENTIAL STRUCTURE 165
SOFT SWITCHES 135-137
SOUND 274-278
SOUND REGISTERS 275
SOURCE CODE 23
SOURCEROR 68-69
SPRITE & SOUND 84, 281-282 84
SPRITE ASSEMBLER 265-267
SPRITE COLOR 254
SPRITE CREATION 251-256
SPRITE ENABLE 253-254
SPRITE EXPANSION 272-274
SPRITE MATRIX 248
SPRITE MOVEMENT 255-256, 272
SPRITE POINTERS 253
SPRITE STORAGE 252
SPRITES 247-274
STA 134, 150
STACK POINTER 108-110
STRUCTURE 165-181
STX 150
STY 150
SUBROUTINES 13-15, 126-128
SYS 25, 37, 46, 59, 193, 228
T-U
TAY146
TOKENS 12
TXA 146-148
TYA 146-148
TYPES OF ASSEMBLERS 8-9
USER GROUPS 290-291
x-z
X REGISTER 105, 143-163, 172, 223
Y REGISTER 105, 143-163, 172, 222
ZERO FLAG 107
ZERO PAGE 160
337
Like printing your own money...
SPECIAL 10 % REBATE OFFER
Now that you have taken the first step toward learning how to program in
assembly language, why not do yourself a favor and get Merlin 64, the best
assembler available for the Commodore 64, and save a little money at the same
time!
Merlin 64 features includes nestable macros, assemble to disk, use of linked
source files, over 35 psuedo opcodes, handy crossreference utilities, a powerful
Editor, and a Monitor to move, compare, disassemble and dump blocks of
memory. Merlin 64 also includes Sourceror, an easy to use disassembler that
creates Merlin 64 source files for editing from binary programs.
And here's the best part. We are making you "an offer you can't refuse!"
That's right! We are offering to pay you to use Merlin 64. Merlin 64 will make
your assembly language programming a breeze, and put some cash back into
your pockets in the process.
And, just like Merlin 64, the rebate is easy to use. Just fill out and send in this
certificate to receive a "ONE TIME ONLY" 10% (ten percent) REBATE of the
net purchase price (excluding state or local taxes) of Merlin 64.
Just send us your original, itemized sales receipt or invoice (as proof of pur-
chase) and the completed Software Registration Form enclosed in each package
accompanied by this certificate and we'll write you a check!
Your receipt will be returned to you along with your rebate check. For your
protection, we recommend that you make a photocopy of the sales receipt prior
to mailing.
(Please print)
NAME:
ADDRESS:.
CITY, STATE, ZIP:
PHONE:* L_
Net Purchase Price $ Rebate Amount Due $
(Your Signature)
OUR GUARANTEE
Roger Wagner Publishing products carry the unconditional guarantee of satisfaction or
your money back. Any product may be returned to place of purchase for complete refund
or replacement within thirty (30) days of purchase if accompanied by the sales receipt or
other proof of purchase.
Roger Wagner Publishing
P.O. Box 582, 10761-E Woodside Ave.
Santee, CA 92071
PH: (619) 562-3670
Rebate offer and prices as of 8/1/84, subject to change without notice.
= = KIDS' ASSEMBLER ON DISK = =
ONLY $10
If you don't feel like keying in the Kids' Assembler, you can get it
cheap from MICROCOMSCRIBE. There's nothing new on the
disk that's not in the book, except it's an inexpensive way to save
time keying in the assemblers, supporting programs and avoiding
typos you might make. The compiled version of the Kids'
Assembler runs a lot faster than the BASIC version.
Here's what you get:
KIDS ASSEMBLER1
KIDS ASSEMBLER2
KIDS ASSEMBLER1-C (Cassette)
KIDS ASSEMBLER2-M (Compiled-Disk only)
SOURCE READER-D (Disk)
SOURCE READER-C (Cassette)
CASSETTE LOADER (Cassette)
HEX-DEC CONVERTER
BINARY-DEC CONVERTER
Fill out the following coupon:
NAME
ADDRESS
CITY
STATE ZIP
Send coupon and $10 to:
MICROCOMSCRIBE
8982 STIMSON COURT
SAN DIEGO, CA 92129
(California residents add 6% sales tax. $10.60 total.)
ONLY disks will be mailed. If you have a cassette system, be sure you can bor-
row a disk drive to download the programs to your tape. (Better yet, save the ten
bucks toward a purchase of a disk drive.)
USER GROUPS and EDUCATIONAL INSTITUTIONS will receive special
considerations. Write MICROCOMSCRIBE for details.
% % % % % % OFFER EXPIRES 12/31/86 % % % % % % % %
ASSEMBLY LANGUAGE
FOR KIDS
COMMODORE 64
by
WILLIAM B. SANDERS
LEARN ASSEMBLY LANGUAGE PROGRAMMING If you'd rather
be one of the kids who writes professional quality arcade games
than one who just plays them, learn machine/assembly language
programming,
WHAT COMPUTER? Everything in this book is for the Commo-
dore 64. You'll get the right information for your computer; not
everyone else's.
WHO'S THIS BOOK FOR? If you know BASIC and want to learn
the fastest language in programming; this book is for you. (If you're
an adult, teli them you got it for your nephew in Borneo.) This is an
elementary book for learning to use assemblers and assembly/
machine language programming on your Commodore 64.
WHICH ASSEMBLER? Three assemblers are fully covered and
most others for the Commodore 64 are compatible with all pro-
grams. Commodore's assembler. The Commodore 64 Macro
Assembler Development System, is clearly explained with lots of
examples. The Merlin assembler is clearly explained with lots of
examples. If you don't have an assembler, there's a simple-to-learn
and use listing of the Kids' Assembler written in BASIC for you free
in the book, The Kids' Assembler assembles programs for you,
and it wiil help you learn about assembly/machine language
programming.
WHAT YOU GET
- An Assembler and instructions on using the most popular Com-
modore 64 assemblers.
- Charts covering everything from hexadecimal - decimal conver-
sions to BASIC tokens to 6510 opcodes.
- Step-by-step clear explanations and clear examples of assembly
language programming.
- Practical utility programs in assembly/machine language you can
write yourself (and understand!).
- Amazing graphics, stupendous sprites, booming sounds, blinding
speed, fame, fortune and a heck of a lot of fun.
Written by, William B. Sanders, the author of the best-selling Ele-
mentary Commodore 64; you will learn assembly language more
simply than you thought possible.
miorocomscribe
literate Microcomputer Documentation ISBN 0—931145-00—7